JavaScript
Article

An Introduction to jQuery’s Deferred Objects

By Aurelio De Rosa

For a long time, JavaScript developers have used callback functions to perform several tasks. A very common example is to add a callback via the addEventListener() function to execute various operations when an event, such as click or keypress, is fired. Callback functions are simple and get the job done for simple cases. Unfortunately, when your web pages increase in complexity and you need to perform many asynchronous operations, either in parallel or in sequence, they become unmanageable.

ECMAScript 2015 (a.k.a. ECMAScript 6) introduced a native means to deal with such situations: promises. If you don’t know what promises are, you can read the article An Overview of JavaScript Promises. jQuery provided and still provides its own flavor of promises, called Deferred objects. They were introduced to jQuery years before promises were introduced to ECMAScript. In this article, I’ll discuss what Deferred objects are, and what problems they try to solve.

A Brief History

The Deferred object was introduced in jQuery 1.5 as a chainable utility used to register multiple callbacks into callback queues, invoke callback queues, and relay the success or failure state of any synchronous or asynchronous function. Since then, it has been the subject of discussion, some criticism, and a lot of changes along the way. A couple of examples of criticism are You’re Missing the Point of Promises and JavaScript Promises and why jQuery implementation is broken.

Together with the Promise object, Deferred represents the jQuery implementation of promises. In jQuery version 1.x and 2.x the Deferred object adheres to the CommonJS Promises/A proposal. This proposal was used as a base for the Promises/A+ proposal which native promises are built upon. As mentioned in the introduction, the reason why jQuery doesn’t adhere to the Promises/A+ proposal is because it implemented promises way before this proposal was even conceived.

Because jQuery was a precursor and due to backward-compatibility issues, there are differences in how you can use promises in pure JavaScript and in jQuery 1.x and 2.x. Moreover, because jQuery follows a different proposal, the library is incompatible with other libraries that implemented promises such as the Q library.

In the upcoming jQuery 3 the interoperability with native promises (as implemented in ECMAScript 2015) has been improved. The signature of the main method (then()) is still a bit different for reasons of backwards compatibility, but the behavior is more in line with the standard.

Callbacks in jQuery

To understand why you might need to use the Deferred object, let’s discuss an example. When using jQuery, it’s very common to use its Ajax methods to perform asynchronous requests. For the sake of the example, let’s say that you’re developing a web page that is sending Ajax requests to the GitHub API. Your goal is to retrieve the list of a user’s repositories, find the most recently updated repository, locate the first file with the string “README.md” in its name and finally retrieve that file’s contents. Based on this description, each Ajax request may only start when the previous step has been completed. In other words, the requests must run in sequence.

Turning this description into pseudocode (please note that I’m not using the real GitHub API), we get:

var username = 'testuser';
var fileToSearch = 'README.md';

$.getJSON('https://api.github.com/user/' + username + '/repositories', function(repositories) {
  var lastUpdatedRepository = repositories[0].name;

  $.getJSON('https://api.github.com/user/' + username + '/repository/' + lastUpdatedRepository + '/files', function(files) {
    var README = null;

    for (var i = 0; i < files.length; i++) {
      if (files[i].name.indexOf(fileToSearch) >= 0) {
        README = files[i].path;

        break;
      }
    }

    $.getJSON('https://api.github.com/user/' + username + '/repository/' + lastUpdatedRepository + '/file/' + README + '/content', function(content) {
      console.log('The content of the file is: ' + content);
    });
  });
});

As you can see in this example, using callbacks we have to nest the calls to perform the Ajax requests in the sequence we want. This makes the code less readable. The situation where you have a lot of nested callbacks, or independent callbacks that have to be synchronized, is often referred to as the “callback hell”.

To make it slightly better, you can extract named functions from the anonymous inline functions I created. However, this change doesn’t help much and we still find ourselves in callback hell. Enter the Deferred and the Promise objects.

The Deferred and the Promise Objects

The Deferred object can be employed when performing asynchronous operations, such as Ajax requests and animations. In jQuery, the Promise object is created from a Deferred object or a jQuery object. It possesses a subset of the methods of the Deferred object: always(), done(), fail(), state(), and then(). I’ll cover these methods and others in the next section.

If you’re coming from the native JavaScript world, you might be confused by the existence of these two objects. Why have two objects (Deferred and Promise) when JavaScript has one (Promise)? To explain the difference and their use cases, I’ll adopt the same analogy I’ve used in my book jQuery in Action, third edition.

Deferred objects are typically used if you write the function that deals with asynchronous operations and which should return a value (which can also be an error or no value at all). In this case, your function is the producer of the value and you want to prevent users from changing the state of the Deferred. The promise object is used when you’re the consumer of the function.

To clarify the concept, let’s say that you want to implement a promise-based timeout() function (I’ll show you the code for this example in a following section of this article). You are the one in charge of writing the function that has to wait for a given amount of time (no value is returned in this case). This makes you the producer. The consumer of your function doesn’t care about resolving or rejecting it. The consumer only needs to be able to add functions to execute upon the fulfillment, the failure, or the progress of the Deferred. Moreover, you want to ensure that the consumer isn’t able to resolve or reject the Deferred at their discretion. To achieve this goal, you need to return the Promise object of the Deferred you’ve created in your timeout() function, not the Deferred itself. By doing so, you ensure that nobody can call the resolve() or reject() method except for your timeout() function.

You can read more about the difference between jQuery’s Deferred and Promise objects in this StackOverflow question.

Now that you know what these objects are, let’s take a look at the methods available.

The Deferred Methods

The Deferred object is quite flexible and provides methods for all your needs. It can be created by calling the jQuery.Deferred() method as follows:

var deferred = jQuery.Deferred();

or, using the $ shortcut:

var deferred = $.Deferred();

Once created, the Deferred object exposes several methods. Ignoring those deprecated or removed, they are:

  • always(callbacks[, callbacks, ..., callbacks]): Add handlers to be called when the Deferred object is either resolved or rejected.
  • done(callbacks[, callbacks, ..., callbacks]): Add handlers to be called when the Deferred object is resolved.
  • fail(callbacks[, callbacks, ..., callbacks]): Add handlers to be called when the Deferred object is rejected.
  • notify([argument, ..., argument]): Call the progressCallbacks on a Deferred object with the given arguments.
  • notifyWith(context[, argument, ..., argument]): Call the progressCallbacks on a Deferred object with the given context and arguments.
  • progress(callbacks[, callbacks, ..., callbacks]): Add handlers to be called when the Deferred object generates progress notifications.
  • promise([target]): Return a Deferred‘s Promise object.
  • reject([argument, ..., argument]): Reject a Deferred object and call any failCallbacks with the given arguments.
  • rejectWith(context[, argument, ..., argument]): Reject a Deferred object and call any failCallbacks with the given context and arguments.
  • resolve([argument, ..., argument]): Resolve a Deferred object and call any doneCallbacks with the given arguments.
  • resolveWith(context[, argument, ..., argument]): Resolve a Deferred object and call any doneCallbacks with the given context and arguments.
  • state(): Determine the current state of a Deferred object.
  • then(resolvedCallback[, rejectedCallback[, progressCallback]]): Add handlers to be called when the Deferred object is resolved, rejected, or still in progress.

The description of these methods gives me the chance to highlight one difference between the terminology used by jQuery’s documentation and ECMAScript’s specifications. In the ECMAScript specifications, a promise is said to be resolved when it’s either fulfilled or rejected. In jQuery’s documentation however, the word resolved is used to refer to what the ECMAScript specification calls the fulfilled state.

Due to the quantity of the methods provided, it isn’t possible to cover all of them in this article. However, in the next sections I’ll show you a couple of examples of the use of Deferred and Promise. In the first example we’ll rewrite the snippet examined in the section “Callbacks in jQuery” but instead of using callbacks we’ll employ these objects. In the second example, I’ll clarify the producer-consumer analogy discussed.

Ajax Requests in Sequence with Deferred

In this section I’ll show how to use the Deferred object and some of its methods to improve the readability of the code developed in the “Callbacks in jQuery” section. Before delving into it, we have to understand which of the available methods we need.

According to our requirements and the list of methods provided, it’s clear than we can use either the done() or the then() method to manage the successful cases. Since many of you might already be accustomed to the JavaScript’s Promise object, in this example I’ll employ the then() method. One important difference between these two methods is that then() has the ability to forward the value received as a parameter to other then(), done(), fail(), or progress() calls defined after it.

The final result is shown below:

var username = 'testuser';
var fileToSearch = 'README.md';

$.getJSON('https://api.github.com/user/' + username + '/repositories')
  .then(function(repositories) {
    return repositories[0].name;
  })
  .then(function(lastUpdatedRepository) {
    return $.getJSON('https://api.github.com/user/' + username + '/repository/' + lastUpdatedRepository + '/files');
  })
  .then(function(files) {
    var README = null;

    for (var i = 0; i < files.length; i++) {
      if (files[i].name.indexOf(fileToSearch) >= 0) {
        README = files[i].path;

        break;
      }
    }

    return README;
  })
  .then(function(README) {
    return $.getJSON('https://api.github.com/user/' + username + '/repository/' + lastUpdatedRepository + '/file/' + README + '/content');
  })
  .then(function(content) {
    console.log(content);
  });

As you can see, the code is much more readable as we’re able to break the whole process in small steps that are all at the same level (in regards of indentation).

Creating a Promise-Based setTimeout Function

As you might know, setTimeout() is a function that executes a callback function after a given amount of time. Both these elements (the callback function and the time) should be provided as arguments. Let’s say that you want to log a message to the console after one second. By using the setTimeout() function, you can achieve this goal with the code shown below:

setTimeout(
  function() {
    console.log('I waited for 1 second!');
  },
  1000
);

As you can see, the first argument is the function to execute, while the second is the amount of milliseconds to wait. This function has worked well for years but what if you need to introduce a delay in your Deferred chain?

In the following code I’ll show you how to use the Promise object that jQuery provides to develop a promise-based setTimeout() function. To do so, I’ll employ the Deferred object’s promise() method.

The final result is shown below:

function timeout(milliseconds) {
  // Create a new Deferred object
  var deferred = $.Deferred();

  // Resolve the Deferred after the amount of time specified by milliseconds
  setTimeout(deferred.resolve, milliseconds);

  // Return the Deferred's Promise object
  return deferred.promise();
}

timeout(1000).then(function() {
  console.log('I waited for 1 second!');
});

In this listing I defined a function called timeout() which wraps the JavaScript’s native setTimeout() function. Inside the timeout() I created a new Deferred object to manage an asynchronous task that consists of resolving the Deferred object after the specified amount of milliseconds. In this case, the timeout() function is the producer of the value, so it creates the Deferred object and returns a Promise object. By doing so, I ensure that the caller of the function (the consumer) can’t resolve or reject the Deferred object at will. In fact, the caller can only add functions to execute, using methods such as done() and fail().

Differences between jQuery 1.x/2.x and jQuery 3

In the first example using Deferred we developed a snippet that looks for a file containing the string “README.md” in its name, but we didn’t account for the situation in which such file isn’t found. This situation can be seen as a failure. When this case happens we might want to break the chain of calls and jump right to its end. To do so it would be natural to throw an exception and catch it with the fail() method, as you would do with the JavaScript’s catch() method.

In Promises/A and Promises/A+ compliant libraries (for example, jQuery 3.x), a thrown exception is translated into a rejection and the failure callback, such as one added with fail() is called. This receives the exception as an argument.

In jQuery 1.x and 2.x an uncaught exception will halt the program’s execution. These versions allow the thrown exception to bubble up, usually reaching window.onerror. If no function is defined to handle this exception, the exception’s message is shown and the program’s execution is aborted.

To better understand the different behavior, take a look at this example taken from my book:

var deferred = $.Deferred();
deferred
  .then(function() {
    throw new Error('An error message');
  })
  .then(
    function() {
      console.log('First success function');
    },
    function() {
      console.log('First failure function');
    }
  )
  .then(
    function() {
      console.log('Second success function');
    },
    function() {
      console.log('Second failure function');
    }
  );

deferred.resolve();

In jQuery 3.x, this code would write the message “First failure function” and “Second success function” to the console. The reason is that, as I mentioned before, the specification states that a thrown exception should be translated into a rejection and the failure callback has to be called with the exception. In addition, once the exception has been managed (in our example by the failure callback passed to the second then()), the following success functions should be executed (in this case the success callback passed to the third then()).

In jQuery 1.x and 2.x, none but the first function (the one throwing the error) is executed and you’ll only see the message “Uncaught Error: An error message” displayed on the console.

jQuery 1.x/2.x

JS Bin on jsbin.com

jQuery 3

JS Bin on jsbin.com

To further improve its compatibility with ECMAScript 2015, jQuery 3 also adds a new method to the Deferred and the Promise objects called catch(). It’s a method to define a handler executed when the Deferred object is rejected or its Promise object is in a rejected state. Its signature is as follows:

deferred.catch(rejectedCallback)

This method is nothing but a shortcut for then(null, rejectedCallback).

Conclusions

In this article I’ve introduced you to jQuery’s implementation of promises. Promises allow you to avoid nasty tricks to synchronize parallel asynchronous functions and the need to nest callbacks inside callbacks inside callbacks…

In addition to showing a few examples, I’ve also covered how jQuery 3 improves the interoperability with native promises. Despite the differences highlighted between old jQuery versions and ECMAScript 2015, Deferred remains an incredibly powerful tool to have in your toolbox. As a professional developer and with the increasing difficulty of your projects, you’ll find yourself using it a lot.

Meet the author
I'm a (full-stack) web and app developer with more than 5 years' experience programming for the web using HTML, CSS, Sass, JavaScript, and PHP. I'm an expert of JavaScript and HTML5 APIs but my interests include web security, accessibility, performance, and SEO. I'm also a regular writer for several networks, speaker, and author of the books jQuery in Action, third edition and Instant jQuery Selectors.
  • http://w3guy.com Agbonghama Collins

    JavaScript is a language I want to master though I have a handful of knowledge about it.

    It was a great read.

Recommended

Learn Coding Online
Learn Web Development

Start learning web development and design for free with SitePoint Premium!

Get the lastest in JavaScript, once a week, for free.