Since the introduction of AJAX, asynchronous operations have become a mainstay of JavaScript applications. The mantra of "never lock the user interface" coupled with JavaScripts single-threaded run loop model means every developer sooner or later deals with callbacks notifying the system of a concluded asynchronous operation. jQueries Deferred objects – introduced in version 1.5 – are a powerful architecture for simplifying dealing with callbacks.

Who should read this document?

In OX App Suite we use Deferred Objects extensively. Whenever our APIs have to carry out an asynchronous operation, most commonly talking to the backend to load or modify something, they return a deferred object. When you write a plugin, you are sometimes asked to return a deferred object to let the surrounding system know when you're done. A good understanding of the Deferred Object is crucial for effectively developing in an OX App Suite context. If you already feel confident wielding and chaining deferreds, you can skip this tutorial.

How to handle asynchronous calls - a bit of history

Traditionally when making an asynchronous call (like an AJAX call), you would directly pass callback methods for the success and error cases to the function. For example, let's find out who you are and pass the callbacks the classic way:

$.ajax({
    url: "/appsuite/api/user?action=get&session=" + ox.session,
    dataType: "json",
    success: function (response) {
        print("Hello there, " + response.data.first_name + "! I'm pleased to make your acquaintance");
    },
    error: function () {
        print("Geebus! An error occurred!");
    }
});
What's wrong with that? Well, nothing really, only once we get to more complicated use cases, this system turns out to be a bit too inflexible.

Now, let's see how this plays out using the deferred object. jQueries #ajax method returns a Deferred Object. On it you can call #done with a callback parameter, for the success callback, and #fail for the error callback:

$.ajax({
    url: "/appsuite/api/user?action=get&session=" + ox.session,
    dataType: "json"
}).done(function (response) {
    print("Hello there, " + response.data.first_name + "! I'm pleased to make your acquaintance");
}).fail(function () {
    print("Geebus! An error occurred!");
});
Looks pretty similar. The strengths of the Deferred Objects way of handling asynchronous operations will become apparent, once we look at more complex use cases. But first: A bit of theory

Consuming deferreds

State management

A Deferred Object can be in one of three states. It starts out in the initial state and is then either resolved, if the operation completed successfully, or rejected. Once the deferred object transition to its resolved state, all the callbacks registered with #done are called. If a callback is registered after the deferred has already been resolved, it will be called immediately with the result of the operation. Conversely the same happens on rejection with the #fail callbacks. This turns out to be a very powerful concept, for something this simple. For one thing, this means an operation can be triggered in a different (typically lower level) layer of your application, and its deferred passed along, possibly already resolved, possibly not, with handlers coming in at their leisure. Let's build a (contrived) example. Instead of displaying the greeting above immediately, we want to do so only when a button is pressed, but we want to start loading the user data immediately anyway. First, the example using the classic way, where we have to manage state ourselves:

var firstName = null;
var cb = null;
$.ajax({
    url: "/appsuite/api/user?action=get&session=" + ox.session,
    dataType: "json",
    success: function (response) {
        firstName = response.data.first_name;
        if (cb) {
            // Looks like someone is waiting, so notify!
            cb(firstName);
        }
    }
});

this.append(
    $("<button>").text("Show greeting").on('click', function () {
        function printGreeting(firstName) {
            print("Hello there, " + firstName + "! I'm pleased to make your acquaintance");
        }
        if (firstName) {
            // Loading has completed, so take action immediately
            printGreeting(firstName);
        } else {
            // Hm, loading hasn't completed yet, so let's stick around
            cb = printGreeting;
        }
    })
);
Next, let's try the deferred way:
var userLoaded = $.ajax({
    url: "/appsuite/api/user?action=get&session=" + ox.session,
    dataType: "json"
});
this.append(
    $("<button>").text("Show greeting").on('click', function () {
        userLoaded.done(function (response) {
            print("Hello there, " + response.data.first_name + "! I'm pleased to make your acquaintance");
        });
    })
);
                    
We don't know (and don't have to know!) when the button is pressed in relation to when the user data has been loaded. We just call the #done method, and the Deferred Objects system takes care of the state management.

Registering multiple handlers

Another advantage of deferred objects is that you can register multiple handlers for each state change. As an example, let's say we want to manage the state of a busy indicator in addition to printing out a greeting. Since loading the current user is pretty fast, we won't see the busy indicator, so we're going to cheat a bit and use a delayed version of the jQuery ajax call (that's otherwise identical). When tackling this in the classic fashion, we might be tempted to do this:

var loadIndicator = $("<div>").css({
    minHeight: "100px"
}).appendTo(this);

loadIndicator.busy();
ctx.delayedAjax({
    url: "/appsuite/api/user?action=get&session=" + ox.session,
    dataType: "json",
    success: function (response) {
        loadIndicator.idle();
        print("Hello there, " + response.data.first_name + "! I'm pleased to make your acquaintance");
    },
    error: function (response) {
        loadIndicator.idle();
    }
});

Probably though, we want a bit more layering, where a low-level API takes care of both loading things and managing the busy indicator (like the one in the OX App Suite toolbar), and the higher level API actually doing something with the data:
var loadIndicator = $("<div>").css({
    minHeight: "100px"
}).appendTo(this);

// Low level
function ajaxWithLoadIndicator(options) {
    // Keep the original callbacks around, so we can call them later
    var realSuccessHandler = options.success;
    var realErrorHandler = options.error;

    // Redefine the success and error callbacks, so we can deal with the loadIndicator before
    options.success = function (response) {
        loadIndicator.idle();
        // Call the original handler
        realSuccessHandler(response);
    };
    options.error = function (response) {
        loadIndicator.idle();
        // Call the original handler
        realErrorHandler(response);
    };

    // Indicate we're loading something
    loadIndicator.busy();
    // make the ajax call
    ctx.delayedAjax(options);
}

// Higher up

ajaxWithLoadIndicator({
    url: "/appsuite/api/user?action=get&session=" + ox.session,
    dataType: "json",
    success: function (response) {
        print("Hello there, " + response.data.first_name + "! I'm pleased to make your acquaintance");
    }
});

The more aspects we want to tack onto a task as it traverses the layers of our application (logging and caching come to mind), the more wrappers we have to build. Let's in turn look at the deferred way of handling this:
var loadIndicator = $("<div>").css({
    minHeight: "100px"
}).appendTo(this);

// Lower level API

function ajaxWithLoadIndicator(options) {
    loadIndicator.busy();
    return ctx.delayedAjax(options)
                .done(function () {
                    loadIndicator.idle();
                })
                .fail(function () {
                    loadIndicator.idle();
                });
}

// Higher up

ajaxWithLoadIndicator({
        url: "/appsuite/api/user?action=get&session=" + ox.session,
        dataType: "json"
})
.done(function (response) {
    print("Hello there, " + response.data.first_name + "! I'm pleased to make your acquaintance");
});
                    
Traversing the layers, other aspects of http handling would simply add more #done and #fail handlers.

Coordinating multiple asynchronous actions

Consider the following scenario: You want to trigger two or more asynchronous operations and only continue once they have all completed successfully. For example, let's load two pieces of information from the backend. As always, we'll implement the 'classic' method first:

var folderData  = null,
    userData    = null;

function allLoaded() {
    if (!folderData || !userData) {
        return;
    }

    print("See the folder data for " + userData.first_name + " " + userData.last_name, folderData);
}

$.ajax({
    url: "/appsuite/api/user?action=get&session=" + ox.session,
    dataType: "json",
    success: function (response) {
        userData = response.data;
        allLoaded();
    }
});

$.ajax({
    url: "/appsuite/api/config/folder?session=" + ox.session,
    dataType: "json",
    success: function (response) {
        folderData = response.data;
        allLoaded();
    }
});

For this use case jQuery provides the $.when(...) method. It expects one or many deferreds and creates a composite deferred. The composite will be resolved once all original deferreds have been resolved, and it will be rejected if any of the original deferreds were rejected (it's an all-or-nothing-deal). To return to our example:

var userLoaded = $.ajax({
    url: "/appsuite/api/user?action=get&session=" + ox.session,
    dataType: "json"
});

var foldersLoaded = $.ajax({
    url: "/appsuite/api/config/folder?session=" + ox.session,
    dataType: "json"
});

$.when(userLoaded, foldersLoaded).done(function (userResponse, folderResponse) {
    // Unpack since the ajax request resolved to three arguments,
    // which are turned into an array and passed as the first and second parameter here
    var userData = userResponse[0].data,
        folderData = folderResponse[0].data;
    print("See the folder data for " + userData.first_name + " " + userData.last_name, folderData);
});
Much shorter. The only somewhat arcane piece about this is the way we have to unpack the variables. Let's fix that by ...

Modifying results in-flight

With deferreds it is possible to register a handler that transforms a result before it is passed on to the done handlers. Let's see this in action:

var userLoaded = $.ajax({
    url: "/appsuite/api/user?action=get&session=" + ox.session,
    dataType: "json"
}).pipe(function (response) {
    // We're only interested in the name
    return response.data.first_name + " " + response.data.last_name;
});

var foldersLoaded = $.ajax({
    url: "/appsuite/api/config/folder?session=" + ox.session,
    dataType: "json"
}).pipe(function (response) {
    // We're only interested in the data attribute
    return response.data;
});

$.when(userLoaded, foldersLoaded).done(function (name, folderData) {
    print("See the folder data for " + name, folderData);
});
The handler you register with #pipe is given the result of the operation, and in turn, whatever the handler returns is passed on to the done handlers.

Creating deferreds

Let's look at the other side of the deferreds equation. Sometimes our plugin system will call on you to create a deferred object to let it know when a operation finished. For example: The portal plugins are expected to implement a #load method, that returns a deferred which resolves to the data needed by the plugin. Oftentimes this just means having another part of our api (for example the HTTP layer) create the deferred, tacking on a pipe handler to adapt the result and passing on the deferred. Sometimes you will have to create a deferred yourself though, so let's look at how this works:

function myAsynchronousFunction(number) {
    // New deferreds can be created simply by calling the $.Deferred() method
    var def = $.Deferred();

    // To simulate an asynchronous operation, we use javascripts setTimeout method.
    // This simulates the delay a true asynchronous operation might incur.
    setTimeout(function () {
        // Let's say this function fails on odd values, and returns a halfed value for even values
        if (number % 2 === 0) {
            // Calling #resolve on the deferred transitions the deferred to resolved state
            // This means all done handlers are called with the value we pass in here
            def.resolve(number / 2);
        } else {
            // Reject on the other hand transitions the deferred object into rejected state and calls all fail handlers
            def.reject("I can only deal with even numbers, and " + number + " does not look even to me");
        }
    }, 2000);

    // Return the deferred object
    return def;
}

print("Wait for it ...");
// Let's test our method
myAsynchronousFunction(42).done(function (result) {
    print("Success! " + result);
}).fail(function (message) {
    print("Failure! " + message);
});

myAsynchronousFunction(23).done(function (result) {
    print("Success! " + result);
}).fail(function (message) {
    print("Failure! " + message);
});

Chaining deferreds

More typically than the above (very contrived) example, you might need to create a new deferred and resolve/reject it based on the state of another deferred. For example, we can more closely tie the AJAX calls we've been making to OX App Suites HTTP API conventions. When the backend runs into an error it returns a JSON object with an error attribute. So, it would be nice if we could build a function that:

  • Fails, when the HTTP protocol call fails
  • Fails, when the server returns an error
  • Succeeds, when the server completes the operation
And here we go:
function callBackend(options) {
    var def = $.Deferred();

    $.ajax(options)
        .done(function (response) {
            if (response.error) {
                // Fail
                def.reject(response.error);
            } else {
                // Success!
                def.resolve(response.data);
            }
        })
        .fail(def.reject); // The reject function can be passed directly as a handler

    return def;
}

// 1) Let's try the protocol error by calling a module that does not exist
callBackend({
    url: "/appsuite/api/atlantis",
    dataType: 'json'
}).done(function (data) {
    print("(first call) Wohoo!", data);
}).fail(function (error) {
    print("(first call) Frowny face :(");
});

// 2) Let's try the backend error by loading a user with an invalid argument
callBackend({
    url: "/appsuite/api/user?action=get&id=-23&session=" + ox.session,
    dataType: 'json'
}).done(function (data) {
    print("(second call) Wohoo!", data);
}).fail(function (error) {
    print("(second call) Frowny face :(");
});

// 3) Finally let's try a successful loading of a user
callBackend({
    url: "/appsuite/api/user?action=get&session=" + ox.session,
    dataType: 'json'
}).done(function (data) {
    print("(third call) Wohoo!", data.display_name);
}).fail(function (error) {
    print("(third call) Frowny face :(");
});

Empty deferreds that resolve immediately

Sometimes you want to immediately pass a value back, but the calling code expects a deferred object. This can work in two fashions:

function myNotQuiteAsynchronousFunction() {
    // Immediately resolve the deferred to the value
    // Sometimes plugin hooks will give plugin code the opportunity
    // to be called asynchronously, but the plugin can comfortably conclude
    // the operation synchronously. In that case simply stick the value into a deferred
    // that you resolve immediately.
    return $.Deferred().resolve(23);
}
or, if you don't even want to pass a value:
function myNotQuiteAsynchronousFunction() {
    // $.when() creates a deferred that is immediately resolved without a result value
    return $.when();
}

Summary

In OX App Suite we use jQuery Deferreds extensively to model and deal with asynchronous operations. This is our quick cheat sheet for deferreds:

  • Asynchronous operations (like HTTP calls or modal dialog style user interactions) return a deferred
  • You can register success handlers with the done method, and error handlers with the fail method on a deferred
  • You can call pipe on a deferred to modify a return value in flight, before it is passed to the success handlers
  • You can combine deferreds by calling $.when(d1, d2, d3, ...) with the deferreds as arguments. This method takes any number of deferreds.
  • You can create your own deferreds by calling $.Deferred. This is sometimes useful if you want to modify the control flow of an asynchronous processing chain.
Finally I can heartily recommend you read up on deferreds in their offical documentation at http://api.jquery.com/category/deferred-object/