Hello! Let's continue our journey into asynchronous JavaScript.
Introduction
In our last lesson, you learned how to create and consume individual Promises using .then(), .catch(), and .finally(). We saw that a Promise is an object representing a future value, which elegantly solves the problem of "callback hell" for a single asynchronous operation. We ended with a fetch example where you saw a .then() followed by another .then(), which I mentioned was called "chaining."
Today, we'll master that exact technique. You will learn how to chain multiple Promises to handle sequential asynchronous operations. This is a fundamental pattern for building applications. For instance, in the e-commerce project you're aiming for, you might need to perform a sequence of actions like:
- Fetch user profile.
- Use the profile ID to fetch their shopping cart.
- Use the cart data to fetch details for each product.
Promise chaining makes this kind of sequential logic clean, readable, and robust.
The Core of Chaining: then Returns a Promise
The magic of promise chaining comes from a simple but powerful rule: every .then() call returns a new Promise. This allows you to attach the next .then() in the sequence, creating a chain.
The value that the next .then() in the chain receives depends on what the previous .then() callback returns:
- If you return a value (e.g., a number, string, or object): The promise returned by
.then()immediately fulfills with that value. - If you return another Promise: The promise returned by
.then()will "wait" for the promise you returned to settle. It will then adopt its state—fulfilling with its value or rejecting with its error.
This second point is the key to handling sequential asynchronous tasks. You perform one async task, and in its .then() block, you return the next async task (which is another promise).
Let's watch a short video that explains this core mechanism.
The ONLY JavaScript Promises Tutorial You’ll Ever Need
This video from Conner Ardman's tutorial, 'The ONLY JavaScript Promises Tutorial You’ll Ever Need', does an excellent job of explaining that .then() returns a promise, which is what makes chaining possible.
Watch the segment from 06:36 to 08:02. Pay close attention to how the return value of one .then() callback becomes the input for the next one.
Chaining Asynchronous Operations in Practice
Now let's apply this to a more practical scenario where we need to make several network requests in a specific order. Imagine fetching a list of to-do items from different files, one after the other.
This next video demonstrates exactly how to structure this.
Asynchronous JavaScript Tutorial #8 - Chaining Promises
In this tutorial, 'Chaining Promises' by Net Ninja, you'll see a clear example of chaining multiple asynchronous function calls. This pattern is very common in web applications.
Watch from the beginning to 03:07. The crucial part is seeing how he returns the getTodos() call from inside the first .then() block. This is what passes control to the next link in the chain.
Here's the pattern you just saw, simplified:
firstAsyncOperation()
.then(result1 => {
// We have the result from the first operation.
// Now, start the second operation and, crucially, return its promise.
return secondAsyncOperation(result1);
})
.then(result2 => {
// This block only runs after secondAsyncOperation is fulfilled.
// We now have the result from the second operation.
console.log(result2);
});
Simplified Error Handling
One of the most powerful features of promise chaining is how it handles errors. You don't need an error handler for every step. Instead, you can place a single .catch() at the end of the chain. If any promise in the chain rejects, the control flow will immediately jump to the nearest .catch() block, skipping any remaining .then() blocks.
Let's see this in action.
Asynchronous JavaScript Tutorial #8 - Chaining Promises
Let's continue with the Net Ninja video. He will now demonstrate how a single .catch() block gracefully handles an error occurring anywhere in the promise chain.
Continue watching from 03:07 to 05:06. Notice how he intentionally creates an error in the third request, and the final .catch() block successfully intercepts it.
This makes your code significantly cleaner and less error-prone than the nested error callbacks required in the pre-Promise era.
A Full Example: The PizzaHub App
To solidify your understanding, let's look at a complete example that mirrors a real-world workflow. This article walks through building a "PizzaHub" ordering system, where each step—finding a shop, checking for pizza availability, getting a beverage, and creating the order—is a separate asynchronous operation.
JavaScript Promise Chain - The art of handling promises
This article, 'JavaScript Promise Chain', provides a fantastic multi-step example. It's a great model for the kind of logic you'd build in an e-commerce application.
First, read the section 'Robin and the PizzaHub Story 🍕' to understand the scenario. Next, quickly scan the mock API functions under 'APIs to Return Promises' to see that each step is a function returning a Promise. Finally, focus on the 'Handle Promises with the Chain' section and study the orderPizza function carefully. This is where you'll see all the pieces chained together.
Common Pitfalls to Avoid
As you start writing your own promise chains, there are two classic mistakes that nearly every developer makes at some point. Being aware of them now will save you hours of debugging later.
-
Forgetting to
returnthe promise: If you call an async function inside a.then()but forget toreturnit, the chain breaks. The next.then()will execute immediately with anundefinedvalue, not waiting for your async operation to finish. -
Nesting promises instead of chaining: This leads you right back to the "Pyramid of Doom" or "Callback Hell" that Promises were designed to solve.
This next resource illustrates these two pitfalls perfectly.
Lesson 6: Asynchronous Programming with Promises | Per Scholas
Let's look at some code that shows exactly what these common mistakes look like and how to fix them.
Find and read the sections titled 'Common Mistake: Not Returning a Promise' and 'Nested Promises (Pyramid of Doom)'. For each, compare the incorrect 'Pyramid' code with the correct flat 'Chain' solution.
The correct, "flat" chain is not just more readable; it's also more maintainable and easier to debug.
Conclusion
You've now covered a crucial pattern in modern JavaScript. By chaining promises, you can orchestrate complex sequences of asynchronous tasks in a way that is logical, readable, and easy to manage.
Key Takeaways:
- Promise chaining is enabled by the fact that
.then()always returns a new promise. - To perform async operations sequentially,
returnthe next promise from within your.then()callback. - A single
.catch()at the end of the chain will handle a rejection from any of the preceding promises. - Always aim for a flat chain of
.then()calls and avoid nesting promises, which leads to the "Pyramid of Doom."
Preview of Next Lesson:
While promise chains are a massive improvement over callbacks, JavaScript offers an even more intuitive way to handle them. In our next lesson, we will explore async/await syntax. It's syntactic sugar built on top of Promises that lets you write asynchronous code that looks and feels almost exactly like synchronous code, further improving readability and simplifying logic.