Skip to content
This repository has been archived by the owner on Feb 13, 2021. It is now read-only.

Commit

Permalink
No commit message
Browse files Browse the repository at this point in the history
  • Loading branch information
inexorabletash committed Nov 7, 2014
1 parent 62f0712 commit a956b1c
Show file tree
Hide file tree
Showing 4 changed files with 67 additions and 40 deletions.
49 changes: 9 additions & 40 deletions @ Indexed DB Promises.md
Expand Up @@ -38,7 +38,7 @@ Transactions grow a `waitUntil()` method similar to [ExtendableEvent](https://sl

The transaction's *active* flag is replaced by a *state* which can be one of: "active", "inactive", "waiting", "committing", and "finished". When a transaction is created the *state* is active. If *state* is "active" at the end of a task then *state* is set to "inactive". If *state* becomes "inactive" and there are no pending requests, *state* is set to "committing" and the transaction attempts to commit. If the transaction successfully commits or aborts, *state* is set to "finished". *NB: This matches the behavior of IDB "v1".*

If `waitUntil(p)` is called and *state* is "committing" or "finished", a new Promise rejected with `TypeError` is returned. Otherwise, *state* is set to "waiting". The transaction now waits on the Promise `p`; if `p` rejects, the transaction aborts. If `p` fulfills, the *state* is set to "committing" and the transaction attempts to commit.
If `waitUntil(p)` is called and *state* is "committing" or "finished", a new Promise rejected with `TypeError` is returned. Otherwise, *state* is set to "waiting". The transaction now waits on the Promise `p`; if `p` rejects, the transaction aborts. If `p` fulfills, the *state* is set to "committing" and the transaction attempts to commit. An explicit `abort()` call still also aborts the transaction immediately, and the promise resolution is ignored.

If a transaction is already waiting on Promise `p` and `waitUntil(q)` is called, then the transaction should instead wait on a new Promise equivalent to `p.then(() => q)`.

Expand Down Expand Up @@ -143,50 +143,19 @@ The requests returned when opening cursors behave differently than most requests

The IDBRequest member `promise()` as defined above already only captures the first success/error result, which for `openCursor()` and `openKeyCursor()` on IDBObjectStore and IDBIndex will effectively be `Promise<IDBCursor?>`. Further iterations are lost.

A few options here:

* `openCursor()` could return a new type `IDBCursorRequest` which does not have promise() but instead an intermediary e.g. some object stream type (which is TBD for the web platform)
* Alternately, we could make `continue()` and `advance()` return `Promise<IDBCursor?>`, akin to https://gist.github.com/inexorabletash/8791448
* In either case, desperately need iteration helpers.
* Need sample code!
```
partial interface IDBCursor {
Promise<IDBCursor?> advance([EnforceRange] unsigned long count);
Promise<IDBCursor?> continue(optional any key);
};
```

The cursor iteration methods (`continue()` and `advance()`) now return `Promise<IDBCursor?>`. *NB: Previously they were void methods, so this is backwards-compatible.* The promise resolves with `null` if the iteration is complete, otherwise it is resolved with the same cursor object with the `key`, `primaryKey`, and `value` attributes will be updated as appropriate, just as with event-based iteration.

### Concerns ###

* With the `waitUntil()` mechanism it is possible to create transactions that will never complete, e.g. `waitUntil(new Promise())`. This introduces the possibility of deadlocks. But this is possible today with "busy waiting" transactions - in fact, locking primitives like Mutexes can already be created using IDB. See https://gist.github.com/inexorabletash/fbb4735a2e6e8c115a7e

* Methods that return requests still throw rather than reject on invalid input, so you must still use try/catch blocks.


### Samples ###

Here's a minimal async key/value store. For simplicity, it doesn't keep a connection open.

```js
function SimpleStorage(name) {
this.name = name;
}
SimpleStorage.prototype = {
_open: function() {
var r = indexedDB.open(this.name);
r.upgradeneeded = function(e) { e.target.result.createObjectStore('store'); };
return r.promise();
},

get: function(key) {
return this._open().then(function(db) {
return db.tx('store').objectStore('store').get(key);
});
},

set: function(key, value) {
return this._open().then(function(db) {
return db.tx('store', 'readwrite').objectStore('store').put(value, key);
});
}
};
```

*TODO: Samples that actually use waitUntil()*


* TODO: Adapt https://gist.github.com/inexorabletash/8791448 into a polyfill for this proposal.
15 changes: 15 additions & 0 deletions cursor_noodling.js
@@ -0,0 +1,15 @@
// Sample, assuming continue() returns Promise<IDBCursor?>

// Without the waitUntil() calls the tx will commit eagerly.

tx.waitUntil(store.openCursor(range).then(iterate));

function iterate(cursor) {
if (!cursor)
return;

console.log(cursor.key, cursor.primaryKey, cursor.value);
tx.waitUntil(cursor.continue().then(iterate));
}


18 changes: 18 additions & 0 deletions explicit_commit.js
@@ -0,0 +1,18 @@
// If you want explicit control over the lifetime of the transaction:

IDBDatabase.prototype.explicitCommitTransaction = function() {
var resolveP;
var p = new Promise(function(resolve) {
resolveP = resolve;
});
var tx = this.transaction.apply(this, arguments);
tx.commit = function() {
resolveP();
};
tx.waitUntil(p);
return tx;
};

var tx = db.explicitCommitTransaction('store', 'readwrite');
// tx will wait indefinitely until the following call is made:
tx.commit();
25 changes: 25 additions & 0 deletions simple_async_storage.js
@@ -0,0 +1,25 @@
// Minimal async key/value store. For simplicity, it doesn't keep a connection open.

function SimpleStorage(name) {
this.name = name;
}

SimpleStorage.prototype = {
_open: function() {
var r = indexedDB.open(this.name);
r.upgradeneeded = function(e) { e.target.result.createObjectStore('store'); };
return r.promise();
},

get: function(key) {
return this._open().then(function(db) {
return db.tx('store').objectStore('store').get(key);
});
},

set: function(key, value) {
return this._open().then(function(db) {
return db.tx('store', 'readwrite').objectStore('store').put(value, key);
});
}
};

0 comments on commit a956b1c

Please sign in to comment.