How jQuery .always() Works with Promises
This article explains how the jQuery .always() method
functions within jQuery's Deferred and Promise architecture. You will
learn the purpose of .always(), how it handles resolved and
rejected states, what arguments it receives, and how it compares to
native JavaScript promise methods for managing cleanup tasks.
What is the jQuery .always() Method?
The .always() method is a callback handler designed for
jQuery Deferred and Promise objects, including
the jqXHR objects returned by $.ajax(). It
allows you to specify a function that executes whenever a promise
settles—regardless of whether it was fulfilled (resolved) or failed
(rejected).
This behavior makes .always() the ideal location for
cleanup logic, such as removing loading spinners, resetting UI states,
or closing connections.
Syntax and Basic Usage
The method accepts one or more functions or arrays of functions as arguments:
promise.always(function(arg1, arg2, arg3) {
// Code that executes regardless of success or failure
});Example with $.ajax()
In an asynchronous request, .always() runs after either
.done() or .fail() handlers finish
executing:
$("#loading-spinner").show();
$.ajax({
url: "/api/data",
method: "GET"
})
.done(function(data) {
console.log("Request succeeded:", data);
})
.fail(function(jqXHR, textStatus, errorThrown) {
console.error("Request failed:", textStatus);
})
.always(function() {
// This runs no matter the outcome
$("#loading-spinner").hide();
});Arguments Passed to .always()
Unlike native JavaScript's Promise.prototype.finally(),
which receives no arguments, jQuery's .always() passes the
outcome arguments directly to your callback.
The exact arguments received depend on how the promise settled:
When Resolved: The callback receives the exact arguments passed to
.done(). For an AJAX call, this is:data: The data returned from the server.textStatus: A string categorizing the status (e.g.,"success").jqXHR: The jQuery XMLHttpRequest object.
When Rejected: The callback receives the exact arguments passed to
.fail(). For an AJAX call, this is:jqXHR: The jQuery XMLHttpRequest object.textStatus: A string categorizing the error (e.g.,"timeout","error").errorThrown: An optional exception object or textual HTTP status.
Because the argument types differ depending on the outcome, checking
the argument types is necessary if you intend to inspect them inside
.always():
$.get("/api/user").always(function(firstArg, textStatus, thirdArg) {
if (textStatus === "success") {
// firstArg is response data
console.log("Success payload:", firstArg);
} else {
// firstArg is the jqXHR object
console.log("Status code:", firstArg.status);
}
});Differences Between .always() and Native .finally()
While .always() predates native ES6 promises, it serves
a similar role to native Promise.prototype.finally(), with
two notable differences:
- Arguments: jQuery
.always()receives resolution or rejection parameters. Native.finally()receives no arguments because it is strictly meant for guaranteed execution regardless of state. - Return Values: In standard native promises,
.finally()returns a new promise that resolves to the original value (unless the finally handler throws an error or returns a rejected promise). jQuery's.always()returns the original Deferred or Promise object to maintain jQuery-style method chaining.