[IndexedDB] Changes to IDBRequest and specification of the success and error events

Hi folks,

We've been playing around with the async API and have made some
changes to the IDBRequest interface that we'd like feedback on and
hopefully inclusion in the spec. Here's what we have now:

  interface IDBRequest : EventTarget {
    void abort();

    const unsigned short INITIAL = 0;
    const unsigned short LOADING = 1;
    const unsigned short DONE = 2;
    readonly attribute unsigned short readyState;

    attribute Function onsuccess;

    attribute Function onerror;
  };

  interface IDBEvent : Event {
    readonly attribute Any source;
  };

  interface IDBSuccessEvent : IDBEvent {
    readonly attribute Any result;
  };

  interface IDBErrorEvent : IDBEvent {
    readonly attribute unsigned short code;

    readonly attribute DOMString message;
  };

First, the obvious stuff. We've moved the error and result property
from the request to their respective events. Having everything on the
event makes it much easier to write callback functions, in my opinion.
Example in a sec. We've also made the success and error event keep a
source property that can be used to get back to the object that
generated the request. This may be a bit confusing but the example
will help. Here we go:

The following code uses the old API to put two values into an object
store and then alert the two keys that were created. We're assuming
that the object store "Data" exists and is an autoIncrementing store.

  var request = indexedDB.open("MyDB", "My Cool Database");
  request.onsuccess = function(event) {
    // request.result is an IDBDatabaseRequest
    var request2 = request.result.openObjectStore("Data");
    request2.onsuccess = function(event) {
      // request2.result is an IDBObjectStoreRequest
      var objectStore = request2.result;
      var request3 = objectStore.put("foo");
      request3.onsuccess = function(event) {
        // request3.result is a key value
        var key1 = request3.result;
        var request4 = objectStore.put("bar");
        request4.onsuccess = function(event) {
          // request4.result is a key value
          var key2 = request4.result;
          alert("All done, keys are " + key1 + " and " + key2);
        };
        request4.onerror = function(event) {
          alert(request4.error.message);
        };
      };
      request3.onerror = function(event) {
        alert(request3.error.message);
      };
    };
    request2.onerror = function(event) {
      alert(request2.error.message);
    };
  };
  request.onerror = function(event) {
    alert(request.error.message);
  };

>From that sample you can see that the error functions are almost
identical but they have to keep track of the proper request that
created them in order to get the right error message.

The success functions also have to keep track of the request that
created them to know what the result property contains. Note also that
we have to save objectStore in request2.onsuccess in order to do an
additional put into the object store in request3.onsuccess. Keeping
track of all of this is really tedious and error prone.

With the changes outlined above, this code can be condensed to the following:

  function errorHandler(event) {
    // event.source is different each time here but can be used to
figure out which operation failed
    // event.code holds the error code
    alert(event.message);
  }
  var request = indexedDB.open("MyDB", "My Cool Database");
  request.onerror = errorHandler;
  request.onsuccess = function(event) {
    // event.source is an IndexedDatabaseRequest
    // event.result is an IDBDatabaseRequest
    request = event.result.openObjectStore("Data");
    request.onerror = errorHandler;
    request.onsuccess = function(event) {
      // event.source is an IDBDatabaseRequest
      // event.result is an IDBObjectStoreRequest
      request = event.result.put("foo");
      request.onerror = errorHandler;
      request.onsuccess = function(event) {
        // event.source is an IDBObjectStoreRequest
        // event.result is a key value
        var key1 = event.result;
        request = event.source.put("bar");
        request.onerror = errorHandler;
        request.onsuccess = function(event) {
          // event.source is an IDBObjectStoreRequest
          // event.result is a key value
          var key2 = event.result;
          alert("All done, keys are " + key1 + " and " + key2);
        };
      };
    };
  };

We haven't done much to compress the length of the script here, but
you'll notice that each success handler is relatively self contained
and doesn't really need to use closures to perform further operations.
There's also no need to remember keep track of whether or not you want
request3 or request2 or request5000. The error handler is much easier
to reuse as well.

So, what do you guys think?

-Ben

Received on Thursday, 6 May 2010 20:40:19 UTC