Promises in Javascript

I spent today implementing CommonsJS/PromisesA on top of Ext-JS. They’re a really powerful metaphor for asynchronous work. Previously I’d made a WorkQueue which took tasks serially. This is still useful where we have a single WorkQueue that producers can keep adding tasks to. Promises are useful where you have complex dependency chains. The one I needed to implement had a number (depending on inputs) of parallel chains which all had to complete before a final task was run. (I was implementing an inversion of control system on top of Ext-JS using Sencha’s class preprocessor chain, so I had to make it asynchronous).

The contract for Promise is very simple

doSomeWork().then(doSomethingWithTheResult);

The asynchronous worker creates a new Promise and holds on to it.

function doSomeWork(){
    var promise = new Promise();

    // Set off the asynchronous job, for example
    Ext.Ajax.request({
        url: 'MyUrl',
        promise: promise,
        success: onSuccess,
        failure: onFailure,
        scope: me
    });   

    return promise;
}

My Promise implementation provides a static method Promise.onAjax() which does all the wiring.
The success handler must call

promise.resolve(result);

And the failure

promise.reject(reason);

The “then” method of Promise allows an action to be taken when the promise is complete. It returns a new Promise which is dependant on the result of the handler. This allows a chain to be made. A failure at the top of the chain is passed down automatically.

This allows a contrived example to output the number 140 to the console:

var promise = new Promise();
promise.then(function(result){ return result + 2; })
       .then(function(result){ return result * 10; })
       .then(function(result){ console.log(result); });
promise.resolve(12);

Promises may be joined, so allowing for parallel queues

Promise.when([p1, p2, p3]).then(......);

The when method returns a promise who’s result is an array of all of the dependency results. If a dependency fails then the failure ‘reason’ is the array of dependency promises.

I have strayed from the idea of the reason being a message that can be printed here, so would be tempted to have “reason” and “further information” as a pair of arguments. That would allow an error handler to always say “log(reason)” on error.

The PromiseA specification does not say how to make the work done by a follow on promise asynchronous. I solved this by having the success handlers take two arguments, the second being the ongoing promise. This allows

promise.then(function(result, ongoingPromise){
    // work with result.
    
    ongoingPromise.defer();
    Ext.Ajax.request(Ext.apply({
        url: 'MySecondRequestUrl'
    }, ongoingPromise.getCallbackArg()));
});

The defer() method causes the ongoing promise to ignore the return from the method. An exception will still result in the promise failing. The getCallbackArg() method returns an object that follows Ext-JS’ conventions for the success and failure handlers and their scope. This is merged into the Ext.Ajax.request call.

The Promise API is quite simple, yet it does provide a strong building block for some quite complete asynchronous workflows.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.