Friday, July 2, 2010

Javascript and Dojo

Since I don't have any theater projects coming up, I figured I might as well fill in the space with what I'm doing at work. My latest gig is working with Javascript and (in particular) the Dojo toolkit to develop some web apps.

In the process of coming up to speed on Javascript and Dojo (neither of which I have used a great deal), I'm hitting Google a lot to learn how the various things work. There is, of course, some level of documentation on everything at the Dojo site, but in some cases it's incomplete, and I have to look elsewhere for someone to explain it better. So in the hopes that maybe someone else will be doing the same sort of thing someday, I'm offering this post to explain how dojo.deferreds work.
Update: Naturally, an updated version of Dojo has made this whole discussion nearly obsolete. The new features in the deferred are more intuitive.

What is a dojo.deferred?

In short, a dojo.deferred is an alternative to threads, a way of having relatively independent parts of your program running at that same time. As the documentation page says, "threads are hard."

Think of it as a chain of functions, which you build up one link at a time. You schedule them in the deferred, and kick it off, and it will work its way through them while the rest of your program continues to run uninterrupted (particularly useful if it's a time-consuming operating like waiting for a response from some remote server). If it reaches the end of its chain, it will sit and wait until you put another link on it.

Each link in the chain is a pair of functions. Which of them is called depends on what the previous link returned: if it returned an error, the errback function of will be called; if it returned a normal value, the callback function will be called. So it is critical that each function return something.

Building the chain

There are four possible functions you can call to add a link to the chain: addCallback, addErrback, addBoth, and addCallbacks, and which you call depends on whether you want to specify just a callback, just an errback, the same function to be run in either case, or separate functions for each. In general, the first link in the chain can be just a callback, because there's no danger of an error preceding it. But subsequent links should include both; if a piece unexepectedly ends in error and you have no errback to handle it, the whole deferred silently grinds to a halt.

Kicking it off

It might not be obvious that you have to actually start the chain processing. It doesn't start chugging immediately, you have to "prime the pump" with something for it to consider as the return value of the previous link. You do this by calling its callback() method, which you pass whatever value you want sent to your first callback.

Example time

I find that most examples are too detailed, so I'm going to keep this one more abstract, so that you see what the deferred is all about and don't get lost in the weeds.


//Let's say we have some functions we're going to chain up
/*
* The deferred is going to call each with the
* result returned by the previous one
*/

function thing_one(i) {
return 'Done with 1';
}
function thing_two(result) {
return 'Done with 2';
}
function thing_three(result) {
return 'Done with 3';
}

/* And here's a generic error function to
* use for every step
*/
function myErrback(e) {
alert("Some step of the deferred returned an error!");
}

// Create the deferred
my d = new dojo.Deferred();
// Chain up the steps
// No chance of failure, so no errback on the first
d.addCallback(thing_one);
d.addCallbacks(thing_two, myErrback);
d.addCallbacks(thing_three, myErrback);
// Kick it off by passing 'Go!' to thing_one
d.callback('Go!');

Note that even after you kick it off, you can add more function pairs to the chain, and they will be processed in the order received. I hope this helps somebody understand this nifty tool.

No comments: