Chaining and Composing Promises
Learn to chain multiple asynchronous operations using .then() and .pipe(), and compose complex workflows by waiting for multiple Promises to complete with $.when().
Chaining and Composing Promises is a free jQuery Academy lesson on CoddyKit — lesson 2 of 3. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the jQuery Academy learning path, one of 3 lessons in the course, and your progress syncs across the web and the CoddyKit app.
Sequence Async Operations
In web development, we often need to perform asynchronous tasks (like fetching data) in a specific order. Chaining promises allows us to do this cleanly.
Instead of nesting callbacks, which can lead to 'callback hell', we can link operations sequentially, making our code much easier to read and maintain.
Chaining with .then()
The .then() method is fundamental for chaining. When a Deferred (or Promise) resolves, its .then() callback is executed. What this callback returns can then be used by the next .then() in the chain.
If the callback returns a new Deferred, the chain waits for that new Deferred to resolve before proceeding.
Simple .then() Chain Example
Let's see a simple example where we simulate two asynchronous steps. The second step only runs after the first one completes, and it uses the data from the first step.
Notice how step1's result ('Data from Step 1') is passed to step2.
function runChainExample() {
let dfd1 = new $.Deferred();
let dfd2 = new $.Deferred();
setTimeout(() => {
console.log("Step 1 complete!");
dfd1.resolve("Data from Step 1");
}, 500);
dfd1.then(function(data1) {
console.log("Received in Step 2: " + data1);
setTimeout(() => {
console.log("Step 2 complete!");
dfd2.resolve("Processed " + data1);
}, 300);
return dfd2;
}).then(function(data2) {
console.log("Received in Final Step: " + data2);
console.log("Chain finished!");
});
}
$(document).ready(function() {
runChainExample();
});Data Flow in .then() Chains
The value returned by a .then() callback determines the input for the next .then().
- If you return a non-Deferred value, that value becomes the resolved value for the next
.then(). - If you return a Deferred object, the chain pauses until that Deferred resolves, and its resolved value is then passed to the next
.then().
Using .pipe() for Transformation
While .then() is the modern standard, jQuery's .Deferred() also offers .pipe(). It's similar to .then() but was originally designed for transforming the resolved or rejected value, or for filtering which Deferred continues the chain.
.pipe() can be used to return a new Deferred that resolves with a transformed value, effectively 'piping' it into the next stage.
.pipe() Transformation Example
Here, we use .pipe() to take the result of the first operation, transform it (e.g., convert to uppercase), and then pass the transformed value to the final .done() handler.
function runPipeExample() {
let dfd = new $.Deferred();
setTimeout(() => {
console.log("Initial data ready.");
dfd.resolve("hello world");
}, 500);
dfd.pipe(function(originalData) {
console.log("Piping: Transforming data...");
return originalData.toUpperCase(); // Transforms the value
}).done(function(transformedData) {
console.log("Final result: " + transformedData);
});
}
$(document).ready(function() {
runPipeExample();
});Composing with $.when()
What if you have multiple asynchronous tasks that are independent but you need to wait for all of them to complete before proceeding? That's where $.when() comes in.
$.when() takes one or more Deferred objects (or Promises) as arguments and returns a new Deferred. This new Deferred resolves only when all the input Deferreds have resolved.
$.when() Parallel Tasks Example
In this example, we simulate two independent AJAX requests (fetchUser and fetchPosts). We use $.when() to wait for both to finish before logging the combined results.
Notice they run in parallel, not sequentially.
function runWhenExample() {
function fetchUser() {
let dfd = new $.Deferred();
setTimeout(() => {
console.log("User data fetched.");
dfd.resolve({ id: 1, name: "Alice" });
}, 800);
return dfd.promise();
}
function fetchPosts() {
let dfd = new $.Deferred();
setTimeout(() => {
console.log("Posts data fetched.");
dfd.resolve([{ id: 101, title: "Post 1" }, { id: 102, title: "Post 2" }]);
}, 500);
return dfd.promise();
}
$.when(fetchUser(), fetchPosts())
.done(function(userData, postsData) {
console.log("All data loaded!");
console.log("User: ", userData);
console.log("Posts: ", postsData);
})
.fail(function() {
console.log("One or more tasks failed.");
});
}
$(document).ready(function() {
runWhenExample();
});Accessing $.when() Results
When the Deferred returned by $.when() resolves, its .done() or .then() callbacks receive the resolved values from each of the input Deferreds as separate arguments, in the order they were passed to $.when().
This makes it easy to combine and process data from multiple sources once all are available.
Promise Chaining Quiz
Consider the following jQuery code snippet:
let dfd1 = new $.Deferred();
let dfd2 = new $.Deferred();
dfd1.then(function(val) {
return val + " World";
}).then(function(newVal) {
console.log(newVal); // Line A
return dfd2;
}).then(function() {
console.log("Finished!"); // Line B
});
dfd1.resolve("Hello");
dfd2.resolve();What is the FINAL output printed to the console, and in what order?
Recap: Chaining & Composing
We've learned how to manage complex asynchronous workflows:
- Chaining with
.then(): Sequences operations, passing results from one stage to the next. .pipe(): An older method for transforming values or mapping Deferreds in a chain.- Composing with
$.when(): Waits for multiple independent Deferreds to complete in parallel before proceeding, providing their results as separate arguments.
These patterns are crucial for building robust and readable asynchronous applications.
Frequently asked questions
Is the “Chaining and Composing Promises” lesson free?
Yes — the full text of “Chaining and Composing Promises” is free to read here on the web, and the jQuery Academy course includes 3 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the jQuery Academy course, upgrade to CoddyKit PRO.
What will I learn in “Chaining and Composing Promises”?
Learn to chain multiple asynchronous operations using .then() and .pipe(), and compose complex workflows by waiting for multiple Promises to complete with $.when(). You practise jQuery Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start jQuery Academy?
No prior experience is required. jQuery Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 2 of 3, so you can start here or from the beginning and move at your own pace.
How long does the “Chaining and Composing Promises” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this jQuery Academy lesson?
Yes. Every jQuery Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- Understanding Deferred Objects
- Chaining and Composing Promises
- Handling Multiple Asynchronous Tasks