What Happens if jQuery $.when() AJAX Call Fails?

When you pass multiple asynchronous AJAX requests to jQuery’s $.when() method, the master Deferred object adopts a "fail-fast" strategy: if even a single AJAX call fails, the entire $.when() operation rejects immediately. This article explains how $.when() behaves during an AJAX failure, which callbacks are executed, and how you can prevent one failing request from breaking the rest of your operations.

The Immediate Rejection Mechanism

jQuery's $.when() behaves similarly to native JavaScript's Promise.all(). It expects all provided Deferred or Promise objects to resolve successfully before it marks the master Deferred as resolved.

If any of the requests encounter an HTTP error (such as a 404, 500, or a network timeout), the following occurs:

  • .done() is aborted: Any callback attached via .done() or the success handler in .then() will not execute, even if all other requests succeed.
  • .fail() triggers immediately: The .fail() callback fires as soon as the first error occurs, without waiting for the remaining active requests to complete.
  • Arguments passed to .fail(): The arguments received by the .fail() handler correspond directly to the specific request that failed, providing the standard jQuery error parameters: jqXHR, textStatus, and errorThrown.
  • Subsequent requests are not canceled: Any other network requests that were in flight when the failure occurred will continue running to completion in the background. However, their results will be completely ignored by the parent $.when() handler.

How to Prevent One Failure from Blocking Others

If your application requires every request to finish regardless of whether individual requests succeed or fail (similar to Promise.allSettled()), you must intercept errors at the individual request level before they reach $.when().

To achieve this, attach a .fail() or .always() handler to each individual $.ajax() call that resolves a surrogate promise or transforms the failure into a resolved state:

function safeAjax(options) {
    var deferred = $.Deferred();

    $.ajax(options)
        .done(function(data) {
            deferred.resolve({ success: true, data: data });
        })
        .fail(function(jqXHR, textStatus, errorThrown) {
            // Resolve instead of reject so $.when() continues
            deferred.resolve({ success: false, error: textStatus });
        });

    return deferred.promise();
}

$.when(safeAjax({ url: '/api/items' }), safeAjax({ url: '/api/users' }))
    .done(function(res1, res2) {
        // This will always run, allowing you to inspect each result individually
        console.log('Result 1:', res1);
        console.log('Result 2:', res2);
    });

By ensuring each underlying Deferred resolves instead of rejecting, $.when() will always fire its .done() callback, giving you full control over how partial failures are handled in your application.