On Dec 22, 2009, at 5:39 PM, Pablo Castro wrote:
Whenever we take a callback that's to be called for each item in a
set (e.g. with a .forEach(callback) pattern), we need a way to
indicate the system whether it's ok to move to the next row and
invoke the next callback or not. Otherwise, in scenarios where the
callback itself performs an operation that doesn't finish
immediately (such as another database async call) the system will
keep queuing up top-level callbacks, which in turn may queue up more
callbacks as part of its implementation, and execution will be in
some order that's very hard to predict at best.
This comes up in several contexts. Applications will often need to
scan more than one object store in coordination. Query processors
will also need this when implementing physical operators for joins
and such. A different context would be a system that needs to submit
an HTTP request per row, where you may want to use an XmlHttpRequest
and unwind after calling open. While the HTTP request is in flight
you don't want to move to the next
In most cases one of the key aspects is that we need separate
components to work cooperatively as they pull rows from one or
multiple scans, and there needs to be a way of controlling the
advance of cursors through the rows.
We would like to introduce pause and resume functions for scans
to support this. Since there is no obvious place to put this right
now, we could introduce an iterator object that can be used to
control things related to the current state of the iteration as of
when the callback happens, or maybe this is the cursor itself.
The resulting code would look like this (the example uses the single-
async-level pattern we're playing around, but these two are actually
independent things):
async_db.forEachObjectInStore(people, function(person, iteration) {
iteration.pause(); // we won't be done with 'person' until later...
var request = async_db.getFromStore(people, person.managerId);
request.onsuccess = function() {
var manager = request.result;
// Do something with both 'person' and 'manager', and now we're
ready to process the next person.
iteration.resume();
};
});
The nice thing about adding these as methods on the side is that
it's completely out of sight in simple scenarios where you may be
just scanning to build some HTML for example. Only if you're doing
multiple coordinated, async tasks you need to know about these
functions.
Regards,
-pablo
The proposed WD can do what you want. You would write the following
code for async processing, even though the solution is not the easiest
to follow:
async_db.request.onsuccess = function() {
var people = async_db.request.result;
people.request.onsuccess = function() {
var cursor = people.request.result;
var eachPerson = function() {
var person = cursor.value;
cursor.request.onsuccess = eachPerson;
people.request.onsuccess = function() {
var manager = people.request.result;
// do something with both person and manager
cursor.continue();
}
cursor.request.onerror = function () {
// do what you will when all the people have been processed
}
people.get(person.managerId);
}
eachPerson();
}
people.openCursor();
}
async_db.openObjectStore(people);
It is difficult to follow the logic above, so I will also give a
synchronous example:
var people = async_db.openObjectStore('people');
var cursor = people.openCursor();
if (cursor)
do {
var person = cursor.value;
var manager = people.get(person.managerId);
// do something with person and manager
} while (cursor.continue());
// we are done with all the people
As I said earlier, there is something to be said about the
comprehensibility of the asynchronous code above, but let's look at
the expressive power separately from ease of programming.
Nikunj
http://o-micron.blogspot.com