Brian's Waste of Time

Mon, 04 Sep 2006

Dojo Deferred and DFS

A while back I talked about the "DataFlowSequence" which Tim and I came up with for dealing with concurrent XHRs. To recap, if you needed a result not yet available you would register interest in it and when it became available it would be yielded to your callback:

var dfs = new skife.ajax.DataFlowSequence()

// register callbacks

dfs.when("first", function(args) {
    log.debug("first received: " + args.first)
});

dfs.when("last", function(args) {
    log.debug("last received: " + args.last)
});

dfs.when("first", "last", function(args) {
    log.debug(["full name:", args.first, args.last].join(" "))
});


// supply data

dfs.supply("first", "Sam")

dfs.supply("last", "Jones")

In the interim Dojo created a Deferred class based on MochiKit's Deferred class, which in turn is based on Twisted's Deferred.

Using Dojo's goes like this:

var d = new dojo.Deferred();

// register callback

d.addCallback(function(name) {
    log.debug("name: " + name)
});


// supply data
d.callback("Sam Jones");

Deferred has an additional registerable callback, an errback, which is called if Deferred#errback(value) is invoked instead of Deferred#callback(value).

var d = new dojo.Deferred();
d.addCallback(function(v) { log.debug("success! : "  + v) })
d.addErrback(function(e) { log.debug("error! : " + v) })


if (Math.random() > 0.5) { 
    d.callback("yea!") 
} 
else { 
    d.errback("boo!") 
}

This would be handled in the DFS via

var dfs = new skife.ajax.DataFlowSequence()

// register callbacks

dfs.when("success", function(args) {
    log.debug("success! : " + args.success)
});

dfs.when("error", function(args) {
    log.debug("error! : " + args.error)
});

if (Math.random() > 0.5) { 
    dfs.supply("success", "yea!") 
} 
else { 
    dfs.supply("error", "boo!")
}

Is fun stuff to hack around with. The Mochikit and Dojo Deferred implementations are almost certainly more performant than the DFS -- they can work with arrays instead of hashes, which should be much quicker. They optimize the common case, wanting one result, and can handle the less common case of wanting several by having deferreds waiting on deferreds. I think I prefer the DFS syntax for needing multiple results, though. I need to play more with deferreds waiting on the results of other deferreds. The single argument nature might be nice to work with, rather than using ~named arguments.

Wheee! Reminds me of a silly hack I have been wanting to do to optimize the syntax of all this. The string value of a JS function includes the declaration, which includes the parameter names. These could be parsed out fairly painlessly, allowing us to do the DFS like:

var dfs = new skife.ajax.DataFlowSequence();

dfs.when(function(first, last) {
    log.debug(["Hello,", first, last].join(" "))
});

dfs.supply("first", "John");
dfs.supply("last", "Flynn")

This would be much slower, but is kind of nice :-)

1 writebacks [/src/javascript] permanent link