demo · v131
Database transactions with await using
The TC39 proposal's motivating use case wasn't any async resource — it was the database transaction. This version uses a real IndexedDB object store: success commits a two-leg transfer, while a thrown error aborts the transaction so the debit is rolled back.
scenario
commit (success)
Both writes queue successfully → [Symbol.asyncDispose] calls IDBTransaction.commit() and waits for complete.
scenario
throw (failure)
The debit write is queued, then the body throws → [Symbol.asyncDispose] calls abort() and the stored balances stay unchanged.
log
the pattern
function idbTransactionScope(db) {
const tx = db.transaction("accounts", "readwrite");
return {
store: tx.objectStore("accounts"),
committed: false,
async [Symbol.asyncDispose]() {
if (this.committed) tx.commit();
else tx.abort();
await transactionFinished(tx);
}
};
}
async function transfer(db, shouldFail) {
await using tx = idbTransactionScope(db);
tx.store.put({ id: "A", balance: 400 });
if (shouldFail) throw new Error("credit leg rejected");
tx.store.put({ id: "B", balance: 150 });
tx.committed = true;
}