jQuery
async
callback
.done()
troubleshooting

Async callback function in jquery .done not executing

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

When jQuery's .done() callback does not execute, the cause is almost always one of these: the AJAX request failed (so .fail() fires instead), the Deferred object was never resolved, the function using .done() does not return a jqXHR/Deferred object, or $.ajax was replaced by fetch which returns a native Promise (not a jQuery Deferred). Understanding the jQuery Deferred/Promise lifecycle — pending, resolved, or rejected — is the key to debugging .done() issues.

How .done() Works

javascript
1// $.ajax returns a jqXHR object (which is a Deferred)
2var jqxhr = $.ajax({
3    url: '/api/data',
4    method: 'GET'
5});
6
7// .done() fires when the request succeeds (HTTP 2xx)
8jqxhr.done(function(data, textStatus, jqXHR) {
9    console.log('Success:', data);
10});
11
12// .fail() fires when the request fails (HTTP 4xx/5xx, network error, parse error)
13jqxhr.fail(function(jqXHR, textStatus, errorThrown) {
14    console.log('Error:', textStatus, errorThrown);
15});
16
17// .always() fires regardless of success or failure
18jqxhr.always(function() {
19    console.log('Request completed');
20});

Cause 1: Request is Failing

The most common reason .done() does not fire is that the request returns a non-2xx status code.

javascript
1$.ajax({
2    url: '/api/data',
3    method: 'GET'
4})
5.done(function(data) {
6    console.log('This never runs');
7})
8.fail(function(jqXHR, textStatus, error) {
9    // THIS runs instead — the server returned an error
10    console.log('Status:', jqXHR.status);      // 404, 500, etc.
11    console.log('Error:', error);               // "Not Found", "Internal Server Error"
12    console.log('Response:', jqXHR.responseText);
13});

Always add .fail() when debugging to see if the request is erroring.

Cause 2: JSON Parse Error

If the server returns invalid JSON but dataType: 'json' is set, jQuery fails to parse it and triggers .fail() instead of .done().

javascript
1// Server returns: "OK" (not valid JSON)
2$.ajax({
3    url: '/api/action',
4    method: 'POST',
5    dataType: 'json'  // Expects JSON response
6})
7.done(function(data) {
8    // Never fires — "OK" is not valid JSON
9})
10.fail(function(jqXHR, textStatus, error) {
11    console.log(textStatus);  // "parsererror"
12    console.log(error);       // "SyntaxError: Unexpected token O"
13});
14
15// Fix: match dataType to actual response format
16$.ajax({
17    url: '/api/action',
18    method: 'POST',
19    dataType: 'text'  // Server returns plain text, not JSON
20})
21.done(function(data) {
22    console.log(data);  // "OK" — now .done() fires
23});

Cause 3: Function Does Not Return the jqXHR Object

javascript
1// WRONG — function does not return the ajax call
2function fetchData() {
3    $.ajax({
4        url: '/api/data',
5        method: 'GET',
6        success: function(data) {
7            console.log(data);
8        }
9    });
10    // Returns undefined
11}
12
13fetchData().done(function(data) {
14    // TypeError: Cannot read properties of undefined (reading 'done')
15});
16
17// CORRECT — return the jqXHR object
18function fetchData() {
19    return $.ajax({
20        url: '/api/data',
21        method: 'GET'
22    });
23}
24
25fetchData().done(function(data) {
26    console.log('Success:', data);
27});

Cause 4: CORS or Network Error

Cross-origin requests fail silently if CORS headers are not set on the server.

javascript
1$.ajax({
2    url: 'https://other-domain.com/api/data',
3    method: 'GET'
4})
5.done(function(data) {
6    // Never fires — blocked by CORS
7})
8.fail(function(jqXHR, textStatus, error) {
9    console.log(textStatus);  // "error"
10    console.log(jqXHR.status); // 0 — network-level block
11});

Check the browser console for CORS errors like "Access-Control-Allow-Origin" header missing.

Cause 5: Using success Callback Instead of .done()

javascript
1// Both work, but mixing them can cause confusion
2$.ajax({
3    url: '/api/data',
4    method: 'GET',
5    success: function(data) {
6        console.log('success callback:', data);
7    }
8}).done(function(data) {
9    console.log('.done() callback:', data);
10});
11// Both fire on success — but if you only define success and forget .done(),
12// chained .done() elsewhere won't see the data

Migrating to Promises (Modern Approach)

javascript
1// Modern fetch API with async/await (replaces jQuery AJAX)
2async function fetchData() {
3    try {
4        const response = await fetch('/api/data');
5        if (!response.ok) {
6            throw new Error(`HTTP ${response.status}: ${response.statusText}`);
7        }
8        const data = await response.json();
9        console.log('Success:', data);
10        return data;
11    } catch (error) {
12        console.error('Error:', error.message);
13    }
14}
15
16// jQuery's .done()/.fail() mapped to try/catch with async/await

Common Pitfalls

  • Not adding .fail() during debugging: Without .fail(), a failed request gives no indication of what went wrong. Always chain .fail() (or .always()) when debugging .done() issues to see HTTP status codes and error messages.
  • dataType mismatch causing parse errors: Setting dataType: 'json' when the server returns HTML or plain text causes a parse error, triggering .fail() instead of .done(). Match the dataType to the actual Content-Type returned by the server, or omit it to let jQuery auto-detect.
  • Not returning the jqXHR from a wrapper function: If your function calls $.ajax() but does not return it, the caller gets undefined and cannot chain .done(). Always return $.ajax(...) from wrapper functions.
  • CORS blocking the request silently: Cross-origin requests blocked by CORS policy show status: 0 in the .fail() callback. Check the browser's Network tab and console for CORS-related error messages.
  • Mixing jQuery Deferred with native Promises: fetch() returns a native Promise, not a jQuery Deferred. Native Promises use .then() and .catch(), not .done() and .fail(). If you migrate from $.ajax to fetch, update the callback pattern accordingly.

Summary

  • .done() only fires on successful (2xx) responses — add .fail() to see errors
  • JSON parse errors (dataType: 'json' with non-JSON response) trigger .fail(), not .done()
  • Always return $.ajax(...) from functions so callers can chain .done()
  • Check for CORS errors in the browser console when .fail() shows status: 0
  • Modern code should use fetch with async/await and try/catch instead of jQuery AJAX

Course illustration
Course illustration

All Rights Reserved.