If you write JavaScript Code, at some point, you will need to handle asynchronous operations. For example, consider an HTTP request of some Web service. Although it's true that HTTP requests can be handled synchronously, your application will perform better if you fully embrace asynchronous operations. As browsers have increased the number of concurrent operations allowed, it's more important than ever to leverage multiple simultaneous requests.

By asynchronous, I mean that a request is made and eventually, that request is fulfilled at some time in the future. The trick is to provide the code that eventually executes when the request has been fulfilled. This is accomplished by supplying a callback function. For example:

var mycallback = function myCallback(data){
    //act on data here.
};

asyncFunction(myCallBack);

You don't know when the myCallBack function will be called because you don't know when the asyncFunction will get around to fulfilling your request. This statement implies that your request may not be fulfilled, which is to say that it will not be completed. What if an error occurs? How would you handle that? What if there are multiple requests? How can you be sure that regardless of what happens, you will get something back? Something more than a single callback function is needed. This is where the Promise pattern comes into play.

The Promise pattern is an approach where we set the stage for something that will happen in the future. The promise is not that your request will be successful. Rather, the promise is that regardless of what happens, an object will be returned. That object has handlers for cases such as success and failure. There are other handlers as well that will be discussed in this article. The key is that regardless of what happens, your calling code will get something back. From there, your code can act accordingly. If the call is successful, your code will proceed on the happy path. On the other hand, if an error occurs, your code can log the error and redirect to a user-friendly error page or give the user a chance to re-initiate the request.

Promises Implemented jQuery.ajax() (v 2.0.3)

The good news is that you don't have to worry about implementing the Promise pattern from scratch. The Promise pattern is fully baked into the jQuery Ajax method and it's that implementation that will be used in this article. For the examples that follow, assume that the following code snippet is used:

$.ajax({url: "mywebservice.com"})

For brevity, I will ignore most of the Ajax settings, with the exception of the URL setting. The Ajax function returns a jQuery XHR object (jqXHR). The jqXHR object is a superset of the native XMLHttpRequest JavaScript object. It's a superset because the jqXHR object has additional functionality over and above the native XMLHttpRequest object. Nevertheless, at its core, the jQuery Ajax abstraction is nothing more than an XMLHttpRequest object.

The jqXHR object supports the following methods:

  • done
  • always
  • fail
  • then

The done, always, and fail methods correspond to old success, complete, and error methods. As of jQuery 1.8, the hold methods have been deprecated in favor of the DAFT interface (done, always, fail, and then). The then interface is the newest and is what you will use to chain promises together.

Applying these comments and leveraging the earlier snippet, the following code illustrates how to apply the done method:

.done(function(data,textStatus, jqXHR){
    //code to handle data from webservice here.
})

If the done method executes, it means that your request was successful. Setting the always method aside for a moment, what if there is an error? To make sure that scenario is handled, you provide the function:

.fail(function(jqXHR, textStatus, errorThrown){
    //code to handle error here.
})

What if you want a block of code to execute regardless of whether or not the request was successful? This is where the always method comes into play:

.always(function(data|jqXHR, textStatus, jqXHR|errorThrown){
    //this code will always execute regardless
    //of whether the done or error method executes
})

Note that the first and third parameters for the always method change depending on whether the done or fail method executed.

Putting it all together, what you have so far is shown in Listing 1.

Listing 1: jQuery Ajax call implementing the Promise interface

$.ajax({url: "mywebservice.com"})
    .done(function(data,textStatus, jqXHR){
    //code to handle data from webservice here.
})

    .fail(function(jqXHR, textStatus, errorThrown){
        //code to handle error here.
})
    .always(function(data|jqXHR, textStatus, jqXHR|errorThrown){
        //this code will always execute regardless
        //of whether the done or error method executes
});

Chaining Promises

If calls result in either success or failure, what is the purpose of the then method? Often, there are multiple operations that have to succeed before proceeding to the next step. You may need to make two or more separate requests where one request is dependent on the success of another. For example, the data from one Web service call might be necessary as input for another Web service call. You can't make the second request until the first request succeeds. This scenario presents a challenge because the whole point of promises and asynchronous development is that you set the stage for something that will happen in the future. You don't know precisely when that request will be fulfilled. Fortunately, this contingency has been accounted for with the then method.

To illustrate this concept, let's start with a custom function that returns the ajax call, which, in turn, returns a promise:

function getData() {
    return $.ajax({url: "mywebservice.com"});
}

Note that the ajax request will not be made until the getData() function is called. Also note that in this call, you don't have the done, always, or fail methods specified. You could specify the methods, but for this example, it isn't required for reasons that will be clear in a moment.

For the second function that takes the data, you could have something like this:

function processData(data, textStatus,jqXHR) {
    return $.ajax({url: "otherservice.com",data: data});
}

The example in Listing 2 joins these calls together with the then method:

Listing 2: Using the then method to join promises

getData()
    .then(processData)
    .done(function(data,textStatus, jqXHR){})
    .fail(function(jqSHR,textStatus, errorThrown){});

The data from the getData Function, assuming the call was successful, will be passed to the processData function. You can chain the call because getData returns what the $.ajax returns, which is a jqXHR object that conforms to the promise interface. If there is a failure, the outer fail function is invoked. The outer fail function is also invoked if the processData function fails. When both the getData and processData functions are successful, the outer done method is invoked.

The previous example works well when there is a known finite number of requests. What if you don't know how many promises have to be chained?

To illustrate, let's assume there is a function that gets an array of items from a Web service:

function getArray() {
    $.ajax({url: "webservice.com"})
    .done(processArray)
    //fail method intentially omitted for brevity
}

What would the processArray function look like? You already know it takes three parameters: data, statusText, and jqXHR. The other thing you know is that it has to process the array and somehow, dynamically join some n-number of promises. The code in Listing 3 will do the trick:

Listing 3: Example of multiple joined deferred promises

function processArray(data, statusText, jqXHR) {

    var promiseArray = [];

    $.each(data,function(index, item){
        promiseArray.push(function(){
            return $.ajax({url: "webservice.com", data: item}) ;
        });
    });

    //With the promise array hydrated, you can invoke the
    //requests

    $.when.apply($, promiseArray)
        .done()
        .fail();
}

The when function itself returns a promise to which a done, always, fail, and then handlers can be applied.

In JavaScript, mastery of asynchronous programming is an absolute must and the Promise pattern is the way to deal with asynchronous challenges as easily and effortlessly as possible.

Implementing Promises in Windows 8 Store Apps

If you are wondering how these concepts apply in JavaScript-based Windows 8 apps, you will find the examples presented in this article are nearly identical to how promises are implemented in Windows 8. The core namespace in a JavaScript Windows 8 application is WinJS. One of the objects in WinJS is the xhr object. Does that name ring a bell? It should, as it's very similar to the jQuery jqXHR object. Both are abstractions around the native JavaScript XMLHttpRequest object and both implement the promise interface and pattern. Just as the jQuery ajax method wraps the XMLHttpRequest object in a promise, so too does the WinJS.xhr method.

Listing 4 is an example of how promises are implemented in WinJS.

Listing 4: Promise implementation in the WinJS object

WinJS.xhr(options).done(function completed(request) {
    // handle completed download.
},
function error(request) {
    // handle error conditions.
},
function progress(request) {
    // report on progress of download.
});

The implementation is slightly different but the core concepts are identical.

If you wish to chain multiple WinJS promises together, try this:

WinJS.xhr(options).then().then().done();

Borrowing from the promise array example earlier, the WinJS.xhr object provides a nice way to join multiple promises together:

WinJS.Promise.join(promiseArray).done();

Each element in the promise array is a separate WinJS.xhr call. Once all the promises have completed, the done method is invoked. Within that done method, you have separate handlers to be invoked if all the joined promises were successful (completed) or if there was an error.

Remember, the promise isn't that your code will always be successful. The promise is that you will get a response back that will indicate success or failure.

Enough Demo-ware. How About a Real-World Example?

A typical scenario includes cases where you have some level of parent/child data. Sometimes, these levels can be three, four, or more levels deep. In the synchronous world, you simply wait for the data request to be fulfilled. In the old Windows Forms and WPF worlds, we waited in a synchronous fashion for the data request to complete.

In the asynchronous world, the job is more complex because there are multiple asynchronous operations that have to be coordinated and completed before you undertake the next action. In the case of parent/child data, before you can get children, you have to know which parents are in the data set. Then, for each parent, you have to get the children. There are ways to structure code so that it takes on synchronous characteristics.

The problem with that is two-fold. First, more code is required and more code leads to more complexity and less maintainability. There's a reason why the first word in the Ajax (Asynchronous JavaScript and XML) acronym is asynchronous. Just about any time you try to make something do something it was not intended to do, friction results. That friction is typically borne of more code that results in more complexity.

The second problem, which is related to the first, is scalability. The more code you have, the more complex it becomes and consequently, the less maintainable it becomes. This presents a scalability problem. There's also a performance problem. If you try to make everything synchronous in a world that was meant to be asynchronous, your applications will not perform well. The recent healthcare.gov debacle has proven that point in many ways.

In this simple example, the data consists of parents that have children. At a raw level, here is the data you will work with:

[{"id":1,"name":"Parent 1"},{"id":2,"name":"Parent 2"}]

[{"id":1,"parentid":1,"name":"Child 1 of Parent 1"},
{"id":2,"parentid":1,"name":"Child 2 of Parent 1"},
{"id":3,"parentid":2,"name":"Child 1 of Parent 2"},
{"id":4,"parentid":2,"name":"Child 2 of Parent 2"}]

The data universe here is very simple: two parents each with two children. To make this hypothetical more complicated (as is often the case in the real world), you don't have the luxury of a single API call that fetches a parent AND its associated children. These are the two API calls you have to work with:

http://localhost:62178/api/parent/getparents
http://localhost:62178/api/child/getchildrenofparent/id

The goal is to get all of the data for the parents and their respective children before another task is initiated. In this case, the task is to display the results on the page.

The code in Listing 5 accomplishes the task:

Listing 5: Using promises and deferred promises to request data

$.ajax({ url: "http://localhost:62178/api/parent/getparents" })
 .error(function(response){//handle error})
 .done(function (parents) {
     var childQueries = [];
     $.each(parents, function (index, parent) {
         childQueries.push(getChildren(parent));
});

$.when.apply($, childQueries)
 .fail(function (response) {
     var html = "A problem was encountered.";
     html += "<br />";
     html += response.responseText;
     $("#output").html(html);
})
.done(function () {
    //now, all of the data (parent and children) of been retrieved.
    var html = "";
    $.each(parents, function (index, parent) {
        html += parent.name;
        html += "<br />";
        if (parent.children != null) {
            $.each(parent.children, function (index, child) {
                html += child.name;
                html += "<br />";
            });
       }

       $("#output").html(html);
   })
});
});

function getChildren(parent) {
    return $.ajax({ url: "http://localhost:62178/api/child/getchildrenofparent/" 
        + parent.id
})
.done(function (children) {
    parent.children = children;
})
.error(function (response) {
    parent.children = null;
});
}

Getting the parents is straight-forward. Once the parents have been retrieved, the done handler is invoked. A then handler could have also been used in this example. Inside the done handler, the code iterates over the parent array. This is where deferred promises are used. In this case, you want to treat the fetching of child data for each parent as a unit of work. Inside that unit of work, the processes are asynchronous. All you care about is when the block of operations has either completed or failed.

This is where the $.when.apply jQuery command comes into play. The childQueries is an array of deferred promises. When $.when.apply is invoked, each promise in the array is fulfilled. Note that throughout the code, failures are both contemplated and handled. This is an important point. Quality code deals with the prospect of failure. The Promise pattern makes it easy to deal with errors. There's no excuse to not have code in place that deals with and handles error conditions.

Once all of the promises have been fulfilled successfully, the done handler for $.when.apply fires. If any one of the underlying promises encounters an error, the fail handler fires. Remember, the promise isn't that your code will always be successful. The promise is that you will get a response back that indicates success or failure. Once all of the data has been retrieved, the next action can be taken.

Usually, you must take the APIs as you find them. Ideally, the APIs you work with will be optimized for easy consumption. In the event they are not, all is not lost. The Promise pattern is here to make your life easier and consequently, your code less complex, easier to understand, and more maintainable. The result? Code that scales.

Remember, if what you're doing seems too difficult and complex, you are probably doing it wrong!

Conclusion

Programming in an asynchronous environment can and often is challenging and difficult when you don't know the tools and techniques available to make your efforts easier. In JavaScript, mastery of asynchronous programming is an absolute must and the Promise pattern is the way to deal with asynchronous challenges as easily and effortlessly as possible. In this article, you learned how promises are implemented in JavaScript under two scenarios: jQuery and WinJS. It is important to note that while JavaScript was used as the learning vehicle, the Promise pattern is not unique to JavaScript. Both .NET and C# implement these concepts via the Parallel Extension Library. Other implementations can be found in node.js, Ruby, Python, C++, and Objective C, to name a few. Although the implementation semantics may differ, the core concepts are the same.