jakearchibald / idb

IndexedDB, but with promises
https://www.npmjs.com/package/idb
ISC License
6.2k stars 348 forks source link

Aborting a Transaction #280

Closed eric1234 closed 1 year ago

eric1234 commented 1 year ago

This is likely not a bug, but me just not understanding how to use this library. But since I didn't see any other place to ask questions I figured I would post as an issue. Assume the following minimal reproduction:

const db = await idb.openDB('db', 1, {
  upgrade(db) { db.createObjectStore('store') },
})
const tx = db.transaction('store', 'readwrite')
await tx.store.put('value', 'key')
tx.abort()

Obviously this is a minimal reproduction. In a real world app I would have done some additional code between the put and the abort which lead to me making the decision to rollback. I have put a runnable version of this on JSBin.

I would expect this to:

The last point is where I have a problem. If you look at your browser console (not the JSBin console) you will see an exception that says "Uncaught (in promise) DOMException: AbortError". This implies there is some promise failing that I am not catching. I have tried surrounding the code with try { ... } catch(e) {} but still no luck. What am I missing?

jakearchibald commented 1 year ago

Probably tx.done?

eric1234 commented 1 year ago

I'm probably just dense but the docs say done "resolves when the transaction completes successfully". I would think if I am implicitly calling abort to rollback the transaction then done does not resolve since the transaction was not successful. I'm thinking of this in terms of SQL transactions where you COMMIT or ROLLBACK a transaction. I assume we either done or abort() the transaction.

I did try putting await tx.done both before and after the transaction just in case but no luck either way. If before then the abort fails because the transaction is already committed. If I await done after the abort then I get the same error.

eric1234 commented 1 year ago

Played with it a bit more and think I have the solution. As you said I need to await for done but it doesn't just resolve it rejects so it causes an exception. So final code is:

const db = await idb.openDB('db', 1, {
  upgrade(db) { db.createObjectStore('store') },
})
const tx = db.transaction('store', 'readwrite')
await tx.store.put('value', 'key')
tx.abort()
try { await tx.done } catch (e) {}

Even found a test that largely has similar code. Not super obvious IMHO but that may just be my own lack of experience with IndexedDB.

Thanks for the pointer! Will close this issue now.

jakearchibald commented 1 year ago

You probably just want to await tx.done, you don't need the try/catch. There can be cases where the put works but the overall transaction fails.

See the example in the docs https://github.com/jakearchibald/idb#txdone

const db = await idb.openDB('db', 1, {
  upgrade(db) {
    db.createObjectStore('store');
  },
});
const tx = db.transaction('store', 'readwrite');
onAbortClick = () => tx.abort();
await Promise.all([tx.store.put('value', 'key'), tx.done]);