jQuery AJAX Complete Callback Explained

The jQuery AJAX complete callback is a predefined function that executes immediately after an HTTP request finishes, regardless of whether that request resulted in a success or an error. It serves as an essential mechanism for running cleanup tasks, updating user interface states, and resetting flags. By understanding how and when the complete function fires, developers can ensure that their web applications maintain a predictable and responsive state after asynchronous communication.

Execution Timing and Parameters

In the standard jQuery AJAX lifecycle, the request proceeds through several stages:

  1. beforeSend: Fires before the request is dispatched.
  2. success or error: Fires depending on whether the server returned a successful HTTP status code (2xx) or an error code (4xx, 5xx), or if the request timed out.
  3. complete: Fires last, after success or error has finished executing.

The complete callback accepts two arguments:

  • jqXHR: The jQuery XMLHttpRequest object containing details about the response, including response headers, status codes, and response text.
  • textStatus: A string indicating the status of the request. Common values include "success", "notmodified", "nocontent", "error", "timeout", "abort", or "parsererror".

Primary Use Cases

1. Hiding Loading Spinners

When an asynchronous operation starts, applications typically display a loading indicator. Because network requests can fail or succeed, hiding the spinner inside both success and error duplicates logic. Placing the hide operation inside complete guarantees that the spinner disappears as soon as the request ends, preventing the UI from getting stuck in a loading state.

2. Re-enabling Form Controls

To prevent duplicate submissions, submit buttons are often disabled when a user initiates a request. The complete callback is the ideal location to re-enable these controls, ensuring users can attempt submission again if an error occurs or proceed after a successful transaction.

3. State Cleanup and Polling

If an application polls a server at fixed intervals, the next poll cycle should often wait until the current one finishes. Triggering the next request inside the complete callback prevents overlapping requests from building up over slow network connections.

Code Example

$.ajax({
    url: "https://api.example.com/data",
    type: "GET",
    beforeSend: function() {
        $("#submit-btn").prop("disabled", true);
        $("#loading-spinner").show();
    },
    success: function(response) {
        $("#content").html(response);
    },
    error: function(jqXHR, textStatus, errorThrown) {
        console.error("Request failed: " + textStatus);
    },
    complete: function(jqXHR, textStatus) {
        // Runs regardless of success or error
        $("#loading-spinner").hide();
        $("#submit-btn").prop("disabled", false);
    }
});

complete Callback vs. .always() Promise Method

In modern jQuery implementations (version 1.5 and above), the jqXHR object implements the Promise interface. While the complete setting inside the $.ajax() configuration remains fully supported, jQuery also provides the .always() method, which performs the exact same role using promise chaining:

$.ajax({
    url: "https://api.example.com/data",
    type: "GET"
})
.done(function(data) {
    // Equivalent to success
})
.fail(function(jqXHR, textStatus) {
    // Equivalent to error
})
.always(function() {
    // Equivalent to complete
    $("#loading-spinner").hide();
});

Both approaches guarantee post-request execution, allowing you to centralize teardown logic without duplicating code across success and failure handlers.