Coordinating Multiple AJAX Requests with jQuery Deferreds and .when()
Learn how to replace nested AJAX callbacks with jQuery Deferreds and $.when() for clearer, maintainable code.
27 Nov 2025, 08:48 UTC

The Problem: Nested AJAX Callbacks
When a page needs to load several independent resources — for example, a user profile and a set of preferences — developers often nest $.ajax callbacks. Each level adds indentation, makes error handling scattered, and obscures the overall flow.
Using jQuery Deferreds to Flatten the Flow
Starting with jQuery 1.5, every $.ajax call returns a Deferred object whose promise() method provides a read‑only view. By wrapping each request in a small helper that returns this promise, you can collect the promises and let $.when() coordinate them.
Helper that returns a promise
function fetchProfile(userId) {
return $.ajax({
url: `/api/users/${userId}`,
method: 'GET',
dataType: 'json'
});
}
function fetchSettings(userId) {
return $.ajax({
url: `/api/users/${userId}/settings`,
method: 'GET',
dataType: 'json'
});
}
Worked Example: Show a Spinner Until Both Requests Finish
Assume a page with a #spinner element and a #content container.
function loadUserData(userId) {
$('#spinner').show();
$('#content').empty();
return $.when(
fetchProfile(userId),
fetchSettings(userId)
).done(function(profileResp, settingsResp) {
// profileResp and settingsResp are arrays: [data, textStatus, jqXHR]
const profile = profileResp[0];
const settings = settingsResp[0];
$('#content').append(
$('').text(profile.name),
$('
').text(`Theme: ${settings.theme}`)
);
}).fail(function(jqXHR, textStatus, errorThrown) {
$('#content').text('Failed to load data: ' + textStatus);
}).always(function() {
$('#spinner').hide();
});
}
// Example usage:
// loadUserData(123);
Trade‑offs and Limitations
- Abstraction overhead: Introducing Deferreds adds a thin layer; for a single request the benefit is minimal.
- Version requirement: Needs jQuery 1.5+; older projects must upgrade.
- Learning curve: Teams unfamiliar with the Deferred API must understand
.done(),.fail(), and.always(). - Mixing with native Promises: Passing a native
Promiseto$.when()works, but mixing without conversion can cause unexpected resolution order; explicitly convert withPromise.resolve()or rely on$.when()to handle it.
How to Verify the Implementation
- Create a simple HTML file that loads jQuery 3.6.0 from a CDN.
- Copy the helper functions and
loadUserDatasnippet into a<script>tag. - Point the AJAX URLs to a public test service, e.g.,
https://jsonplaceholder.typicode.com/users/1for the profile andhttps://jsonplaceholder.typicode.com/users/1(or another endpoint) for settings. - Open the browser console; you should see no errors and the spinner disappear once both requests finish.
- To test error handling, change one URL to return a 500 (e.g., append
/invalid) and verify that the.fail()callback runs and the spinner is hidden.
Actionable Closing
If you find yourself nesting AJAX callbacks, start by extracting each request into a function that returns its $.ajax promise. Then use $.when() to coordinate them, attaching .done(), .fail(), and .always() handlers for a clean, linear flow. This pattern scales to three or more parallel requests and keeps error handling centralized, making the code easier to read and maintain.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.