Journal

Using jQuery when() with AJAX

JQuery's AJAX is an essential part of a web developers toolbox since the ability to do asynchronous requests powers much of today's web applications. In this tutorial, I will go through how to use jQuery.when() to handle callbacks based on multiple asynchronous objects.

If that all sounded like gibberish, here's a quick overview. AJAX stands for Asynchronous Javascript and XML. It allows web pages to be changed and updated without reloading the whole page. An example is the Google search box - the search box dropdown updates with suggestions based on what you typed. Behind the scenes, there is a Javascript listener that sends off an AJAX query to the Google servers whenever something is entered, with the results rendered in the form of a dropdown option.

Using jQuery's AJAX, a typical request might look like this:

$.ajax({
    url: "http://server.com/path"
}).done(function(data) {
    console.log("Received " + data);
});

The .done() method is only executed when the $.ajax() is completed successfully. It optionally takes a callback function as input, like in the example shown. In this case, the AJAX makes a GET request to http://server.com/path then prints out the received data to the console.

There are some situations where it is desirable to do two or more AJAX requests, then run another callback after they have all been completed. For example, you may want to show a loading screen while fetching data from multiple sources then hide it when the requests have been completed. That's where jQuery.when() comes in.

The example from jQuery docs example goes something like this:

$.when(
    $.ajax("/page1.php"),
    $.ajax("/page2.php")
).done(function() {
    // do something
});

...which is fine except that I typically write my $.ajax() as functions for reusability. More problematically, this doesn't work:

$.when(
    some_function(),
    another_function()
).done(function() {
    console.log("This should appear last");
});

function some_function() {
    $.ajax({
        url: "http://server.com/path"
    }).done(function(data) {
        console.log("some_function completed");
    });
}

function another_function() {
    $.ajax({
        url: "http://server.com/path"
    }).done(function(data) {
        console.log("another_function completed");
    });
}

This gives the unexpected output:

This should appear last
some_function completed
another_function completed

What gives?

A closer reading of the jQuery.when() docs will reveal that it is expecting one or more Deferred objects. The $.ajax() implements the Promise interface, making it a Promise-compatible object (which is a subset of the Deferred object), so $.when() knows what to do with it when the Promise methods are called. This explains why $.when($.ajax(...)) works.

So why doesn't the second example work?

$.when() is actually given two functions that don't return anything. Since the input is not a Deferred or a Promise, $.when() will be treated as resolved and the .done() callback is triggered immediately.

To fix this, the input functions must return a Deferred object. In this case by returning the $.ajax() object:

$.when(
    some_function(),
    another_function()
).done(function() {
    console.log("This should appear last");
});

function some_function() {
    return $.ajax({
        url: "http://server.com/path"
    }).done(function(data) {
        console.log("some_function completed");
    });
}

function another_function() {
    return $.ajax({
        url: "http://server.com/path"
    }).done(function(data) {
        console.log("another_function completed");
    });
}

Now when we run it again, it returns the expected:

some_function completed
another_function completed
This should appear last

There is also a working example on JSFiddle.

Last modified Feb. 3, 2016, 12:28 a.m.

Published Feb. 3, 2016, 12:03 a.m.


blog comments powered by Disqus