STATUS: Obsolete - see this updated proposal instead.
Extend IDBDatabase with:
interface IDBDatabase {
IDBExplicitTransaction explicitTransaction(scope, mode);
};
Which gives you one of these:
interface IDBExplicitTransaction : IDBTransaction {
Promise<undefined> commit(); // Resolves if committed, rejects if aborted; idempotent
};
An explicit transaction remains active until it aborts due to a request error event on which preventDefault()
is not called, or abort()
or commit()
is explicitly called by script.
This allows you to write things such as:
function queryFetchAndStore(key) {
return new Promise(resolve, reject) {
var tx = db.explicitTransaction(['urls', 'blobs'], 'readwrite');
var r = tx.objectStore('urls').get(key);
r.onerror = reject;
r.onsuccess = function() {
// If tx were not explicit, it would autocommit when we went async here.
fetchAsBlob(r.result).then(function(blob) {
// ... and even if not, the tx would not be active here so would throw
tx.objectStore('blobs').put(blob, key);
tx.commit().then(resolve, reject);
}, reject);
};
};
}
That's still pretty gnarly entangling of Promises with Events. Let's clean it up a bit.
interface IDBRequest {
Promise<any> ready(); // Returns new Promise that resolves/rejects with success/error
Promise<any> then(f, g); // Shortcut for ready().then(f, g);
Promise<any> catch(f); // Shortcut for ready().catch(f);
};
With the caveat that ready()
throws unless the request's transaction is explicit. Maybe? Otherwise the transaction is inactive inside the resolve/reject callbacks.
(Also there's probably a caveat about cursors.)
Now you can write:
function queryFetchAndStore(key) {
var tx = db.explicitTransaction(['urls', 'blobs'], 'readwrite');
return tx.objectStore('urls').get(key).then(function(url) {
return fetchAsBlob(url);
}).then(function(blob) {
tx.objectStore('blobs').put(blob, key);
return tx.commit();
});
}
This is optional sugar:
interface IDBTransaction {
Promise<any> then(f, g); // Shortcut for commit().then(f, g);
Promise<any> catch(f); // Shortcut for commit().catch(f);
};
- An explicit transaction presumably aborts when it is GC'd. Does this make GC observable, e.g.
tx.addEventListener('abort', function() { alert('gc!'); });
- We'd probably have to let them get GC without firing the event (otherwise, what is
event.target
). But GC would still be observable if there was another transaction blocked on it. - Something similar applies to the
event.waitUntil(p)
primitive in Service Workers. - Hrm... this is already sort-of exposed by IDB:
- We'd probably have to let them get GC without firing the event (otherwise, what is
indexedDB.open('db', 1).onsuccess = function(e) { var db = e.target.result; db = null; };
indexedDB.open('db', 2).onsuccess = function() { alert('db was collected'); };
-
Since functions (e.g.
get()
) still throw for invalid inputs, this is still not a very Promise-like API (see https://github.com/w3ctag/promises-guide) -
Cursors?