A guide to using jQuery Promises and Deferred

Posted by : at

Category : guides   tutorials


When you tackle frontend development in JavaScript, a very common challenge (even in 2020) is to deal with writing code that is a mix of synchronous and asynchronous function calls.

What are these?

If you haven’t come across the lingo before, a synchronous function call is one in which the JavaScript will wait for a execution to complete before continuing. The following snippet is such a case; using the window.alert.

// a synchronous function call
window.alert("nothing happens until you click ok");
console.log("now I can run");

Notice that the now I can run text won’t display in the browser console until you dismiss the alert.

An asynchronous function, on the other hand, will allow the JavaScript to continue execution while waiting for the asyncronous function to complete - at some point in the future.

A typical example of this is waiting for data from a database. Perhaps the information of the current user of your application:

// an asynchronous few lines
console.log('about to make the async call');
var user = $.get('/api/user/info');
console.log('this is empty', user);

If you were to run the above code, you would see that the user object displayed by the console would be undefined, as this is executed immidiately after our call to fetch the info.

(As an aside, this is a very common error to watch out for in a codebase. It’s very easy to write some code with an assumption that a function is synchronous, when it is in fact - asynchronous.)

What’s the answer?

The answer is Promises. Promises is an approach to dealing with the challenges of asynchronous function execution. There are a few explanations for the origin of the term, but they mostly focus around the notion that eventually when you make a promise - it is fulfilled; in other words, when you execute an asynchronous method, it will eventually complete.

There are several Promise-based libraries, and depending on your browser, they are now even supported “natively”. However you can’t always rely on this support if you’re writing web software that can be run by yesterday’s, present day, future day’s browsers.

jQuery Deferred

The jQuery library offers a Deferred object, which provides access to an internally managed promise:

function doSomethingAsync() {
    var d = $.Deferred();
    // do something async
    return d.promise();
}

In this function snippet, we’re creating a Deferred object, then returning the internal promise to the function caller.

But how do we notify the function caller that the promise has been fulfilled (either successfully or failed)? We use the resolve() and reject() methods.

Note that these two methods will accept anything for a parameter which will just be returned to the callee. So it’s typical to include the result (or the error) from your asynchronous function call.

Let’s expand the doSomethingAsync() snippet to demonstrate:

function doSomethingAsync() {
    var d = $.Deferred();
    var jqxhr = $.get('/api/user');
    jqxhr.done(function(result) {
        d.resolve(result);
    });
    jqxhr.fail(function(err) {
        d.reject(err);
    });
    jqxhr.always(function() {
        console.log('I always run after either done() and fail()');
    });
    return d.promise();
}

Before we go further, let’s break that down. Within doSomethingAsync() we’re creating the Deferred jQuery object. The function then starts execution of an asynchronous fetch of some user account. Meanwhile, we’re returning the Deferred objects’ promise.

If the asynchronous function call succeeds (aka. our server located the account we wanted), then within the done() function, we use the Deferred.resolve() method to complete our promise and return the result back to the callee.

If the asynchronous function call fails (aka. our server is “down” or the account we wanted doesn’t exist), then within the fail() function we use the Deferred.reject() method to complete the promise and return the error result back to the callee.

Once either of the done() or fail() methods are called, another function handler known as always() will execute.

We typically would setup something like the following snippet:

var p = doSomethingAsync();
startUXSpinnerAnimation();
p.done(function(result) {
    console.log('the user account was found', result);
});
p.fail(function(err) {
    console.error('the user account was not found', err);
});
p.always(function() {
    console.log('I always run after either done() or fail()');
    stopUXSpinnerAnimation();
});

Hopefully you can now see how to start structuring your asynchronous function calls with the help of jQuery’s Deferred object and Promises.

It’s about the only “reliable” way to coordinate animations displayed to the user, and the network calls happening under the hood.

There are many different Promise libraries and updates to the JavaScript language itself (see await and async), but it’s still extremely useful to understand every approach possible to dealing with asynchronous functions.

You never know what will await you in the next codebase you work on…

and THAT is a promise! (oh dear).

About Erik Yuzwa

My name is Erik Yuzwa. I'm a full stack web developer and software engineer from Calgary, Alberta.

Categories