How to Use jQuery $.when() for Multiple AJAX Calls

Managing asynchronous JavaScript operations often requires waiting for several network requests to complete before executing subsequent logic. jQuery provides the $.when() method, which accepts multiple Deferred or Promise objects—such as those returned by $.ajax()—and triggers a callback only when all requests have resolved. This guide explains how to structure multiple concurrent AJAX calls using $.when(), process their returned datasets, handle dynamic lists of requests, and catch potential errors.

The Basic Syntax

Every $.ajax() call returns a jqXHR object, which implements the Promise interface. By passing these objects as arguments into $.when(), you can attach a .done() handler that fires only after every request completes successfully.

const requestUsers = $.ajax({ url: '/api/users', method: 'GET' });
const requestPosts = $.ajax({ url: '/api/posts', method: 'GET' });

$.when(requestUsers, requestPosts).done(function(usersResult, postsResult) {
    // Both requests are finished
    console.log('Users Data:', usersResult[0]);
    console.log('Posts Data:', postsResult[0]);
});

Understanding the Response Arguments

When passing multiple Deferreds to $.when(), the .done() callback passes an array of three arguments for each request:

  1. data: The actual payload returned from the server.
  2. statusText: A string describing the status (e.g., "success").
  3. jqXHR: The raw jQuery XMLHttpRequest object.

To access the server response directly, read the first element of each argument (e.g., usersResult[0]). If only a single Deferred is passed to $.when(), the callback arguments behave like standard $.ajax().done() arguments rather than an array.

Handling a Dynamic Number of AJAX Calls

When the number of requests is not known in advance, collect the AJAX promises into an array and pass them into $.when().

Using the ES6 Spread Operator

In modern JavaScript environments, use the spread operator (...) to pass the array elements as individual arguments:

const endpoints = ['/api/users', '/api/posts', '/api/comments'];
const requests = endpoints.map(url => $.ajax({ url: url, method: 'GET' }));

$.when(...requests).done(function(...responses) {
    responses.forEach((res, index) => {
        console.log(`Response ${index}:`, res[0]);
    });
});

Using Function.prototype.apply()

For older environments without ES6 support, use .apply():

var requests = [
    $.ajax({ url: '/api/users' }),
    $.ajax({ url: '/api/posts' })
];

$.when.apply($, requests).done(function() {
    // Arguments correspond to the requests array
    for (var i = 0; i < arguments.length; i++) {
        console.log('Data:', arguments[i][0]);
    }
});

Error Handling with .fail()

By default, $.when() operates with "fail-fast" behavior. If any individual AJAX request fails, the entire chain immediately rejects and invokes the .fail() callback, skipping .done():

$.when(
    $.ajax('/api/valid-endpoint'),
    $.ajax('/api/broken-endpoint')
)
.done(function(res1, res2) {
    console.log('All requests succeeded');
})
.fail(function(jqXHR, textStatus, errorThrown) {
    console.error('At least one request failed:', textStatus, errorThrown);
});

If you need all requests to complete regardless of individual success or failure, handle errors within the individual $.ajax() definitions using .catch() or standard error callbacks before passing the promises to $.when().