O'Reilly logo

Learning jQuery Deferreds by Nicholas H. Tollervey, Terry Jones

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Chapter 1. Introduction

A deferred represents a result that may not be available yet. It is an abstraction for something that has yet to be realized.

We attach code to the deferred to take care of the expected or erroneous result when it becomes available.

That’s it!

Deferred usage is very similar to the way we make plans: when X finishes, if there was no error, do Y, otherwise do Z. To give an everyday example, “when the tumble dryer finishes, if the clothes are dry, fold them and put them away, otherwise hang them on the line.” Here, “the tumble dryer finishes” is the deferred, “fold them” and “put them away” are handlers for the good case (also known as callbacks) and “hang them on the line” is the handler for an error condition (sometimes known as an errback). Once the plan is made, we’re free to get on with something else.

Although the outcome of the deferred is undetermined, we can plan ahead for two possibilities: the clothes are either going to be wet or dry when the tumble dryer finishes. The dryer may actually already be finished, but that does not impact our planning. The important thing to note is that deferreds provide a clear separation between initiating something (resulting in a deferred) and handling the result (when the deferred completes). There are many advantages from this clean separation, and in this book we’ll explore them.

Don’t panic if this all seems a bit abstract; there are plenty of examples coming right up.

Food for Thought

JavaScript programs operate in an event-based environment. Let’s be clear about what that means. Keystrokes, mouse clicks, and low-level I/O operations completing are all events. As your program runs, it can, in advance, tell the JavaScript runtime about events it is interested in and provide code to execute when such events occur. Later, when events relevant to your program happen, the JavaScript runtime will invoke the code you wrote to handle them.

This is a simple, efficient, and familiar model. It closely matches the way we plan ahead in our daily lives. Most of us could quickly make a breakfast of fresh orange juice, toast, and boiled eggs by preparing all three items at once. We know that we’ll have time to squeeze the oranges while the toast and the eggs are cooking, so we’ll get them both cooking first. We know what to do, regardless of whether the toast or the eggs are cooked first. On a grander scale, consider the kitchen staff of a busy restaurant. By initiating long-term actions (e.g., putting water on to boil), by reacting to events (e.g., the cheese on a dish is browning), and by switching among other tasks in the meantime, a small team can efficiently prepare a wide range of dishes for a large number of simultaneous diners.

In event-based programming (and not only in JavaScript), handling single events is trivial. Coordinating code to handle multiple events, though, can be very challenging. Ad hoc solutions are often awkward to construct, difficult to test, brittle, hard for others to follow, and depressing to maintain.

The problem rears its head even in trivial situations. For example, suppose you have a JavaScript food API available, with makeToast and makeEggs functions. Both accept a callback function that they call once their product is done, passing the finished result as an argument. An example call looks like:

makeToast(function(toast){
    // Toast is ready!
});

Your challenge is to write a function called makeBreakfast that gets the toast and the eggs cooking simultaneously and that calls a single callback when both are ready.

function makeBreakfast(callback){
    // Use makeToast and makeEggs to make toast and eggs simultaneously.
    // When both are ready, pass them to callback.
}

Pause now, please, and think about how you’d implement makeBreakfast.

Here’s a common strategy: when either underlying function (makeToast or makeEggs) finishes, check to see if the other result is also available. If so, call the callback. If not, store the result so the termination of the other function can pass it to the callback. The resulting code isn’t elegant and doesn’t generalize well. What if making breakfast expands to also include making coffee and pancakes?[1]

This is an extremely trivial example of managing multiple events, yet our code is already a mess. Real-world scenarios are almost always more complex, and can of course be much more complex.

If you had to solve problems like the above a few times, you’d soon see a general pattern. You’d likely write a helper function or two. And if you did that, you’d be well on your way to implementing deferreds!

Terminology: Deferreds and Promises

We need to get a little terminology clear from the very beginning: the difference between deferreds and promises in jQuery.[2]

Continuing with our food theme, the first edition[3] of Twisted Network Programming Essentials by Abe Fettig (O’Reilly) gives a beautiful analogy of deferreds in the real world. Some popular restaurants use remotely activated buzzers to let diners know when a table is available. This avoids a physical queue of waiting customers clogging up the entrance to the restaurant and allows future diners to enjoy a drink at the bar or a short walk in the interim. This elegant approach moves us from a problematic and boring synchronous solution (waiting in line) to an asynchronous one that lets everyone get on with other things in the meantime.

When the maître d’hôtel puts your details (number of diners, seating preference, etc.) into the restaurant’s system, he or she is taking the first in a series of steps that will result in you eventually getting a table. In jQuery terminology, the maître d’ creates a deferred. You are handed a buzzer, which corresponds to a promise. At some point in the future, when a table becomes free, the maître d’ will push a button or click a mouse to “resolve” the deferred and the buzzer will go off in your pocket. Importantly, you (the holder of the promise), cannot cause the buzzer to go off. Only the maître d’ (the holder of the deferred) can do that.

With jQuery deferreds, things work in exactly the same way. The programmer writing a function that needs to get some slow work done (for example, a database operation or a network call) creates a deferred and arranges to fire it when the result of the work becomes available. From the deferred a promise is obtained and returned to the caller. Just like the diner with the buzzer, the caller cannot cause the promise to fire. Just as future diners can have a drink at the bar, a program that receives a promise can get on with other computations instead of twiddling its thumbs while waiting for the promise to fire.

To summarize: create deferreds but return promises.

Familiar Promises

If you’ve ever used $.ajax in jQuery or used any of the animate methods, you’ve already used a promise. For example, you may have written:

$('#label').animate({ opacity: 0.25 }, 100, function(){
    // Animation done.
});

The return value of the animate function gives you a way to get a promise that is resolved when the animation finishes. You could instead have written:

var promise = $('#label').animate({ opacity: 0.25 }, 100).promise();

promise.done(function(){
    // Animation done.
});

That may not seem like a big deal, but what if you want to coordinate what happens after two animations have finished? Using promises, it’s trivial:

var promise1 = $('#label-1').animate({ opacity: 0.25 }, 100).promise();
var promise2 = $('#label-2').animate({ opacity: 0.75 }, 200).promise();

$.when(promise1, promise2).done(function(){
    // Both animations are done.
});

The jQuery $.when method can accept multiple promises and return a new one that will let you know when all the passed promises have resolved. Contrast the simplicity of the above with the breakfast-making shenanigans in Food for Thought.

The $.ajax method returns a value that also has promise methods. So, you could write the following:

$.when($.ajax('http://google.com'), $.ajax('http://yahoo.com')).then(
    function(googlePage, yahooPage){
        // Both URLs have been fetched.
    }
);

It’s easy to do more complex things, e.g., fetch the contents of two URLs, run an animation after each loads, and then do something else when all four events are finished:

$.when(
    $.ajax('http://google.com').then(function(){
        return $('#label_1').animate({ opacity: 0.25 }, 100);
    }),
    $.ajax('http://yahoo.com').then(function(){
        return $('#label_2').animate({ opacity: 0.75 }, 200);
    })
).then(
    function(){
        // Both URLs have been fetched and both animations have completed.
    }
);

Notice how the code almost reads like a natural language description of a simple plan.

Unfortunately, deferreds have a reputation for being abstract and difficult to understand. As we’ve seen though, they’re not! They’re conceptually very close to the way we naturally think about and plan for future events.

The basic understanding of deferreds provided in this chapter is all you need to enjoy some of the mind-bending, elegant, and downright fun ways in which deferreds can make event-based programming so challenging and rewarding. We’ll see a ton of examples of using deferreds in Chapter 3. But before we do that, we’ll need to learn about the jQuery deferred API.



[1] See when for jQuery’s solution.

[2] Note that jQuery’s terminology (and implementation) is slightly different from other packages, some of which do not use the term “deferred” at all. At some point you might like to read the Wikipedia article on “Futures and promises”.

[3] Jessica McKellar was added as an author in the second edition and the nice analogy was removed. Our reference is to the second edition.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required