Promises with dojo – a lightweight alternative to dojo.Deferred

Posted by filed under Dojo Love, Experiments in Web.

Update: Micheil Smith had a cool idea to improve this and posted it in the comments. I have updated the test page and the code examples to reflect his idea. Thanks Micheil!

Update II: Ben Hockey proposed to do the whole thing without using the pub/sub system. I for one think its a good idea, but in case you want the topic version, I’ll leave the test page and add another one with the topic-less version. Thanks Ben!

Update III: Dojo 1.5 introduces robust Promises with dojo.deferred as Krys Zyp explains in this SitePen post.

One of the things/ideas/concepts that I really like about CommonJS is Promises.

Promises? A brief explanation from the CommonJS API:

Promises provide a well-defined interface for interacting with an object that represents the result of an action that is performed asynchronously, and may or may not be finished at any given point in time.

To hear more about Promises, I highly recommend these two posts.

That concept is not entirely new. The dojo toolkit has dojo.Deferred, which does a similar job. You create a Deferred, add (multiple) callbacks and error handlers to it, and when it fires off, all your handlers get called. But dojo.Deferred is pretty heavyweight. It’s loads of code. And it works with .addCallback() and .addErrback() methods – not with .then() methods, which provide maximum readability.

The goal

What I want to have is the follwing code running (with dojo):

asyncComputeTheAnswerToEverything().
    then(addTwo).
    then(printResult, onError);

(This is the example code from the proposed CommonJS Promise API)

And I want the execution to continue right after doSomethingAsnyc() – the async call should still be non-blocking. The .then() methods should recieve the results of the previous part of the chain, but still be non-blocking. Luckily, dojo provides a tool that allows us to achieve this: the publish/subscribe system.

Let’s do it

When we have unique topics for each promise made, we can subscribe the .then() method to the current topic in the chain, and publish the next topic when the data handlers are done computing. Within the then() method we need to check if the data we recieve is an error or not, to determine wich handler we need to call. The spec also demands a progress handler – if we wanted to implement that, we needed to add another topic, but I left that out.

We only need to kick off  the whole thing when we create the first promise, then the rest takes care for itself. Including the changes proposed by Micheil, what you have to do is pretty trivial and you don’t have to care about unique topics or the pub/sub system at all. The asyncComputeTheAnswerToEverything() function mentioned above would look like the following:

var asyncComputeTheAnswerToEverything = function(){
	var promise = new dojox.Promise();
	window.setTimeout(function(){ // some async operation
		var data = 42;
		promise.emit(data);
	},2000);
	return promise;
};

You create a Promise and store a reference to it. You return the promise and use the reference to it to kick off the chain when the data is there. Everything else runs on it’s own now.
Go to the test page or theand check the source – it contains the Promise code and the example functions. Open the console and run testSpec(); to start off the example code from the CommonJS API proposal.

Usage

As a namespace to put the Promise into I chose dojox – but choose whatever floats your boat. You can just drop the promise code somewhere into your code, or, if want to dojo.require("dojox.Promise") it, create a file named Promise.js that contains the code and place a dojo.provide("dojox.Promise"); as the first line in it. Save the file in the dojox directory and you’re done. (Well, to be honest, this violates the dojox naming convention, but if its just in your project, it’s ok and it will work.)

Any questions left? Feel free to leave a comment, @ me at twitter or drop me a line.

References / Files / More

9 Responses to Promises with dojo – a lightweight alternative to dojo.Deferred


  1. Some how I don’t think this is the nicest API that could be used. Surely something like http://gist.github.com/293569 would be better? It wouldn’t be too hard to implement and the code size difference would be minimal. It also lowers the level for figuring out how promises work and should be used.


  2. Here’s a version I quickly created based off what you have: http://gist.github.com/293591


  3. @Micheil Smith

    Yes, that looks great! I really like the .emit() idea. It doesn’t add too much to the code, and it makes it far more readable and easier to use/understand.

    Thanks for this, I’ll update the post with your modifications later today :)


  4. you could also skip the pub/sub and just use ‘then’ to dojo.connect to ’emit’. emit would be an empty function and ‘then’ would use connect in place of subscribe. iirc, pub/sub uses connect under the covers.


  5. @ben hockey

    Interesting idea. I guess what you are aiming at is this:
    http://gist.github.com/295287

    *Big* pro of that: No more unique id handling, no private _promiseId value.

    A drawback would be that you’d lose the possibility to distinguish the promises by that id and you couldn’t hook onto the topics being published.

    Bottom line is… hm, hard to say, but, personally, I’ go for the non-topic, direct-connect version you came up with because it is simpler and shorter.

    Cool idea!


  6. Incidentally a possible “event”-based implementation was discussed in Dojo-Dev (http://article.gmane.org/gmane.comp.web.dojo.devel/12371) — look for onCallback. That article contains other views and unanswered questions on a possible design of Promise.

    I agree with you 100% that we have to find a synergy between different pieces of the toolkit (in this case AOP and Promise) in order to have an elegant cohesive solution, which is flexible and easy to use, rather than a bunch of independent yet inflexible and inconsistent blotches of code.


  7. @Jens Arps and @ben hockey:

    I agree with Ben’s version, it’s much cleaner then my original modification.


  8. @Eugene Lazutkin

    Thanks for the pointer. Pretty in-depth conversation, so I’ll have to continue reading it later ( == weekend ).

[Comments are automatically closed after 30 days.]

By continuing to use the site, you agree to the use of cookies. more information

The cookie settings on this website are set to "allow cookies" to give you the best browsing experience possible. If you continue to use this website without changing your cookie settings or you click "Accept" below then you are consenting to this.

Close