Keywords: jQuery | Deferred | Promise | .then() | .done() | Asynchronous Programming | Chaining
Abstract: This article provides an in-depth exploration of the core differences between the .then() and .done() methods in jQuery Deferred objects. Through version evolution analysis, it details the behavioral changes of the .then() method before and after jQuery 1.8, transitioning from simple syntactic sugar to a Promise-returning method with filtering and chaining capabilities. The article combines code examples to demonstrate the multi-callback feature of .done(), the chain propagation mechanism of .then(), and practical application scenarios in asynchronous operation orchestration, offering clear usage guidance for developers.
Introduction
In jQuery's asynchronous programming model, Deferred objects and the Promise pattern provide robust support for handling asynchronous operations. Among these, the .then() and .done() methods are commonly used for attaching callbacks. While superficially both handle successfully completed operations, they exhibit significant differences in underlying mechanisms and usage scenarios. This article systematically analyzes the core distinctions between these two methods from three perspectives: historical version evolution, functional特性对比, and practical application examples.
Basic Concepts and Version Evolution
In jQuery, a Deferred object represents an operation that has not yet completed but will do so in the future, with states including resolved, rejected, and pending. A Promise is a read-only view of a Deferred, used to attach callbacks without altering its state.
Prior to jQuery 1.8, the .then() method was essentially syntactic sugar, functionally equivalent to consecutive calls to .done() and .fail(). Specifically:
promise.then(doneCallback, failCallback)
// was equivalent to
promise.done(doneCallback).fail(failCallback)This design allowed developers to specify both success and failure callbacks in a more concise manner. However, starting with jQuery 1.8, the behavior of .then() underwent a fundamental change. It is no longer mere syntactic sugar but became an alias for the .pipe() method and returns a new Promise object. This change endowed .then() with enhanced capabilities, including value filtering and chain operations.
Functional特性对比 Analysis
Core特性 of .done() Method
The .done() method is specifically used to attach callback functions that execute when the Deferred object is resolved. Its core特性 include:
- Multi-callback Support:
.done()can accept multiple callback functions as arguments, which execute in the order they were added. Non-function parameters are automatically filtered out, for example:promise.done(fn1, fn2, true, [fn3, [fn4, 56, fn5], "omg", fn6], fn7); // Only fn1, fn2, fn3, fn4, fn5, fn6, fn7 are added to the callback list - No Return Value Propagation: Return values from callback functions do not affect subsequent
.done()calls; each callback receives the original resolved value. - State Specificity: Handles only the resolved state, with
.fail()correspondingly handling the rejected state.
Modern Implementation of .then() Method
Since jQuery 1.8, the .then() method exhibits the following important特性:
- Returns New Promise: Each call to
.then()returns a new Promise object, enabling chainable calls. - Value Filtering and Transformation: Callback functions can return new values, which are passed as arguments to the next
.then()callback in the chain. - Comprehensive State Handling: Supports optional
doneFilter,failFilter, andprogressFilterparameters for complete state management.
Empirical Analysis of Chaining Behavior Differences
Chaining behavior is one of the most notable differences between .then() and .done(). The following comparative examples clearly illustrate this disparity:
// Example of chain propagation with .then()
var promise = $.Deferred().resolve("abc");
promise.then(function(x) {
console.log(x); // Output: "abc"
return 123;
}).then(function(x) {
console.log(x); // Output: 123
}).then(function(x) {
console.log(x); // Output: undefined
});// Example of no propagation with .done()
var promise = $.Deferred().resolve("abc");
promise.done(function(x) {
console.log(x); // Output: "abc"
return 123;
}).done(function(x) {
console.log(x); // Output: "abc"
}).done(function(x) {
console.log(x); // Output: "abc"
});From the output, it is evident that .then() forms a genuine call chain, where the return value of each callback becomes the input parameter for the next. In contrast, all callbacks of .done() receive the same original resolved value, and return values are ignored.
Practical Applications in Asynchronous Operation Orchestration
The chaining特性 of .then() make it excel in orchestrating complex asynchronous operations, particularly in scenarios requiring sequential or parallel processing of multiple asynchronous tasks.
Sequential Asynchronous Operations
The following example demonstrates how to use .then() to implement sequential HTTP requests:
function sequentialRequests() {
return $.get('/api/first').then(function(result1) {
console.log('First request completed:', result1);
return $.get('/api/second?value=' + result1);
}).then(function(result2) {
console.log('Second request completed:', result2);
return $.get('/api/third?value=' + result2);
}).then(function(result3) {
console.log('All sequential requests completed:', result3);
return result3;
});
}Parallel Asynchronous Operations
For scenarios requiring parallel execution and result merging, .then() can also handle it elegantly:
function parallelRequests() {
var promise = $.Deferred().resolve("start");
return promise.then(function() {
var request1 = $.get('/api/data1').then(function(result) {
console.log('Request 1 completed:', result);
return result;
});
var request2 = $.get('/api/data2').then(function(result) {
console.log('Request 2 completed:', result);
return result;
});
// Use $.when to wait for all parallel requests to complete
return $.when(request1, request2).then(function(result1, result2) {
return {
data1: result1,
data2: result2
};
});
}).then(function(combinedResult) {
console.log('All parallel requests completed:', combinedResult);
return combinedResult;
});
}Association with Ajax-Specific Methods
It is important to note that .success() and .error() are not general methods of Deferred objects but are specific aliases for the jqXHR object (the return value of Ajax requests):
var jqXHR = $.ajax('/api/data');
// The following equivalences hold true
jqXHR.done === jqXHR.success
jqXHR.fail === jqXHR.errorThis design allows Ajax calls to use more semantic method names, but these methods do not exist on generic Deferred objects.
Version Compatibility Considerations
When choosing between .then() and .done(), consider the jQuery version used in the project:
- jQuery < 1.8:
.then()as syntactic sugar with limited functionality - jQuery ≥ 1.8:
.then()with full chaining and filtering capabilities - Cross-version Compatibility: For supporting older versions, it is advisable to use a combination of
.done()and.fail()
Conclusion and Best Practices
Through in-depth analysis of .then() and .done(), the following usage recommendations can be derived:
- Simple Callback Scenarios: Use
.done()when only simple success callbacks are needed without chain propagation. - Complex Asynchronous Flows: Use
.then()when value transformation, error handling, or multi-step asynchronous operation orchestration is required. - Version Adaptation: In jQuery 1.8+ environments, prioritize
.then()for enhanced functionality. - Code Readability: Choose the method that best expresses intent based on the specific scenario to improve code maintainability.
Understanding the fundamental differences between these two methods aids developers in making more appropriate technical choices when faced with various asynchronous programming needs, leading to more robust and maintainable JavaScript code.