Make transaction work with async callbacks.

Author: KpjCompCreated Sep 18, 2024Updated Oct 17, 2025
Labelsduplicateenhancementwontfix

Came across this problem when using Drizzle, and found transactions were not actually rolling back.

The issue is that wrapTransaction always assumes the callback is sync, in this day of JS that's often not going to be the case, eg. during the transaction you might want to read a file, make a http request etc, without using blocking methods.

One solution is check if the result is a Promise, if use then & fail to handle the commit / rollback. This should then mean sync requests continue as is, and Promise version is then correctly handled too. eg.

javascript
// Return a new transaction function by wrapping the given function
const wrapTransaction = (apply, fn, db, { begin, commit, rollback, savepoint, release, rollbackTo }) => function sqliteTransaction() {
	let before, after, undo;
	if (db.inTransaction) {
		before = savepoint;
		after = release;
		undo = rollbackTo;
	} else {
		before = begin;
		after = commit;
		undo = rollback;
	}

	const ok = (r) => {
		after.run();
		return r;
	}

	const fail = (ex) => {
		if (db.inTransaction) {
			undo.run();
			if (undo !== rollback) after.run();
		}
		throw ex;
	}

	before.run();
	try {
		const result = apply.call(fn, this, arguments);
		if (result instanceof Promise) return result.then(ok).catch(fail)
		return ok(result);
	} catch (ex) {
		fail(ex);
	}
};