Promises in Ext.js and Sencha Touch

Do you feel like passing callbacks and setting scopes everywhere makes your code messy and difficult to follow?

Promises can make your code more readable, easier to debug and understand, and also a easier to test.

What is a promise though?

Simple promises

The idea of a promise is that you request someone to do something for you and in return they’ll give you a promise that it will be done and with which they will notify you. The simple flow looks like this:

    // get promise
    var promise = getUserDetails("matt");
    // register a listener to be notified when it's done
    promise.then(function(mattsDetails) {
        // got some data back - now we can
        // deal with it
    });

Compare this to callbacks:

    getUserDetails("matt", function(mattsDetails) {
        // got some data back - now we can
        // deal with it
    });

It’s pretty similar to promises right – so where’s this benefit I’m proposing? Well let’s look at a more complex, but realistic example of callbacks. Let’s imagine we have a backend that:

  • Gets details about the current user
  • gets tweets for that user
  • renders those tweets into the dom
  • Oh and if an error happens at any point we should pop up a message:

    getUserDetails("matt", function(mattsDetails) {
        getTweetsForUser(mattsDetails, function(tweets) {
            addTweetsToUi(tweets);
        }, function(error) {
            showUiErrorMessage(error);
        });
    }, function(error) {
        showUiErrorMessage(error);
    });

I’ve seen this kind of code a lot (I’ve written this kind of code too), but you know what, we can simplify this a bit:

    getUserDetails("matt", function(mattsDetails) {
        getTweetsForUser(mattsDetails, addTweetsToUi, showUiErrorMessage);
    }, showUiErrorMessage);

In fact I would usually pull out the functions to avoid nested function hell – it helps me understand the code better. So let’s see if this makes it easier to follow:

    function handleMattsDetails(mattsDetails) {
        getTweetsForUser(mattsDetails, addTweetsToUi, showUiErrorMessage);
    };
    getUserDetails("matt", handleMattsDetails, showUiErrorMessage);

Hmm To me this way of passing around callbacks still feels hard to understand. It doesn’t seem easy to follow the code

The promises way

Let’s just take a look at how a promises implemented version would look:

    getUserDetails("matt")
           .then(getTweetsForUser)
           .then(addTweetsToUi, showUiErrorMessage);

Ok this seems nice. Why is it better though? Well to me this just reads better, like a sentence:

“get user details (for) matt, then get tweets for (that) user, then add (the) tweets to (the) ui (or) show (a) ui error message”

I believe I could sit down at some code like this and understand what the code is trying to do with little cognitive effort. In addition I have only one error handler, this is quite a nice feature of promises, in that errors are propagated to the last error handler if they’ve not been handled before that.

So how does it work when writing the functions that return a promise?

The promises API in detail

Let’s investigate the then function we’ve been using:

    promise.then(successHandler, errorHandler);

The Promise object will have a then function with which you register one or two callbacks. The first callback will be invoked when the promise succeeds and will pass in any data as the arguments. The second callback is invoked when an error occurs and can be passed an object with details of the error.

When you’re writing code that uses promises there is an expectation that you will return a promise to the invoker of your function. To write an Ext.js function which returns a promise your code would look like:

    function getUserDetails(username) {
        var promise = new Promise();
        Ext.Ajax.request({
            url: "/user/details",
            params: {
                username: username
            },
            success: function(response) {
                var userDetails = Ext.decode(response.responseText);
                promise.resolve(userDetails);
            },
            failure: function(response) {
                promise.reject("Could not get user details - server response was " + response.status);
            }
        });
        return promise;
    }

The key bits here are the:

    promise.resolve(userDetails);

which will cause the successHandler registered to be called and given the userDetails object. Should our AJAX request fail we do:

    promise.reject("Could not get user details - server response was " + response.status);

This causes the errorHandler to be called with the given message.

Let’s do a comparison to the version implemented with callbacks:

    function getUserDetails(username, successHandler, errorHandler) {
        Ext.Ajax.request({
            url: "/user/details",
            params: {
                username: username
            },
            success: function(response) {
                if (Ext.isFunction(successHandler)) {
                    var userDetails = Ext.decode(response.responseText);
                    successHandler(userDetails);
                }
            },
            failure: function(response) {
                if (Ext.isFunction(errorHandler)) {
                    errorHandler("Could not get user details - server response was " + response.status);
                }
            }
        });
    }

To be honest there isn’t too much difference, but let’s list the differences that there are:

  1. Firstly we have to test for the two extra params and ensure they’re functions – technically we don’t, but it’s a good practice to do to avoid runtime exceptions being thrown.
  2. A more subtle issue occurs when an api changes – for example if you wanted to refactor and add a new parameter, let’s say userDetails were versioned on the server and we wanted to be able to pull back a specific version, if no version is given we should pull back the latest.

    In the promises implementation we can add an extra param to the end and if it’s not set assume we want the latest, the signature looks like:

            function getUserDetails(username, version) {
    

    We know that we don’t need to update any code when we add this because all our old code just passed in the username. However in the callback version in order to maintain backwards compatibility we’d need to add the param at the end:

        function getUserDetails(username, successHandler, 
                                errorHandler, version) {
    

    Hmm this just feels wrong, we could solve this by creating a new implementation call getUserDetailsVersioned or refactor all existing calls to pass in null before our callbacks. In the end though it feels like we’re working around a bad pattern.

One other key point is that if you want to chain your then functions then you just need to return promise’s from each of the functions. Therefore:

    getUserDetails("matt")
           .then(getTweetsForUser)
           .then(addTweetsToUi, showUiErrorMessage);

needs getUserDetails and getTweetsForUser to return a promise. However I’d suggest returning a promise from addTweetsToUi too – who knows when you may need to add a notification hook to the after render of the tweets.

Where do I get a Promises implementation from?

There are several implementations available. I personally use rsvp.js: https://github.com/tildeio/rsvp.js. However there are others out there.

What about testing? You said it was easier to test with

Yes I did… But I’m going to save that for another blog post

2 thoughts on “Promises in Ext.js and Sencha Touch

Leave a Reply