This is a brilliant piece of code that takes advantage of the powerful new JavaScript 1.7 features opened by Firefox, and since IndexedDB is only supported by Firefox and Chrome, I would say this is a great compromise.
The first line of code creates a generator from the testSteps
function and assigns it to the testGenerator
variable. The reason we use generators is because IndexedDB is a purely asynchronous API; and asynchronous programming and nested callbacks are a pain. Using generators alleviates this pain by letting you write asynchronous code that looks synchronous.
Note. . If you want to know how to use the power of generators for synchronous asynchronous code, read the next article .
To explain how generators are useful for providing asynchronous programming, consider the following code:
var name = "Test"; var version = 1.0; var description = "Test database."; var request = mozIndexedDB.open(name, version, description); request.onupgradeneeded = function (event) { var db = event.target.result; var objectStore = db.createObjectStore("Thing", { keyPath: "id", autoIncrement: true }); var object = { attributeA: 1, attributeB: 2, attributeC: 3 }; var request = objectStore.add(object, "uniqueID"); request.onsuccess = function (event) { var id = event.target.result; if (id === "uniqueID") alert("Object stored."); db.close(); }; };
In the above code, we requested a database called Test
. We requested database version 1.0
. Since it did not exist, the onupgradeneeded
event onupgradeneeded
was started. As soon as we received the database, we created an object store on it, added the object to the object store, and after saving it, we closed the database.
The problem with the above code is that we query the database and perform other operations related to it asynchronously. This can make the code very difficult to maintain as more and more nested callbacks are used.
To solve this problem, we use generators as follows:
var gen = (function (name, version, description) { var request = mozIndexedDB.open(name, version, description); request.onupgradeneeded = grabEventAndContinueHandler; var event = yield; var db = event.target.result; var objectStore = db.createObjectStore("Thing", { keyPath: "id", autoIncrement: true }); var object = { attributeA: 1, attributeB: 2, attributeC: 3 }; request = objectStore.add(object, "uniqueID"); request.onsuccess = grabEventAndContinueHandler; event = yield; var id = event.target.result; if (id === "uniqueID") alert("Object stored."); db.close(); }("Test", 1.0, "Test database."));
The grabEventAndContinueHandler
function grabEventAndContinueHandler
defined after the generator as follows:
function grabEventAndContinueHandler(event) { gen.send(event); }
The generator starts as follows:
gen.next();
After starting the generator, a request is created to open a connection to this database. Then, grabEventAndContinueHandler
attached as an event handler to the onupgradeneeded
event. Finally, we return or pause the generator using the yield
keyword.
The generator automatically resumes when the gen.send
method gen.send
called from the grabEventAndContinueHandler
function. This function simply takes one argument named event
and sends it to the generator. When the generator resumes, the passed value is stored in a variable called event
.
To repeat, the magic happens here:
// resume the generator when the event handler is called // and send the onsuccess event to the generator request.onsuccess = grabEventAndContinueHandler; // pause the generator using the yield keyword // and save the onsuccess event sent by the handler var event = yield;
The above code allows you to write asynchronous code as if it were synchronous. To learn more about generators, read the next MDN article . Hope this helps.