Promise Chaining
MedPromise chaining allows sequential asynchronous operations to be written in a flat, readable structure. Each .then() returns a new Promise, allowing chains of dependent async operations. Returning a value passes it to the next .then(); returning a Promise waits for it to settle. Mastering chaining is key to writing clean async code.
Interactive Visualization
CodeStart
1fetch("/api/user")2 .then(response => response.json())3 .then(data => console.log(data));
Promise Pipeline
1
P1 (fetch)
PENDING
Console Output
-
fetch() returns a Promise (P1) - starts pending while network request happens
1 / 5
Key Insight: Each .then() waits for the previous Promise to settle. The chain executes sequentially, step by step.
Key Points
- .then() always returns a new Promise
- Return a value to pass it to the next .then()
- Return a Promise to wait for it before continuing
- Errors skip to the next .catch()
- Breaking the chain: forgetting to return
- Chaining enables sequential async without nesting
Code Examples
Basic Chaining
getUser(1) .then(user => { console.log('Got user:', user.name); return getOrders(user.id); // Return Promise }) .then(orders => { console.log('Got orders:', orders.length); return orders[0]; // Return value }) .then(firstOrder => { console.log('First order:', firstOrder); }) .catch(error => { console.error('Any step failed:', error); }); // Flat structure, each step depends on previous
Each .then() receives the value returned by the previous .then().
Returning Promises Waits
fetchUser(1) .then(user => { // Returning a Promise pauses the chain until it settles return fetchOrders(user.id); // Waits for orders }) .then(orders => { // This runs AFTER fetchOrders completes console.log(orders); return fetchProducts(orders[0].id); }) .then(products => { console.log(products); }); // Sequential execution, not parallel
When you return a Promise from .then(), the next .then() waits for it to complete.
The Classic Return Mistake
// ❌ WRONG: Forgetting to return fetchUser(1) .then(user => { fetchOrders(user.id); // No return! }) .then(orders => { // orders is undefined! // This runs immediately, not waiting for fetchOrders }); // ✅ CORRECT: Always return fetchUser(1) .then(user => { return fetchOrders(user.id); // Return the Promise! }) .then(orders => { // orders has the actual data });
Forgetting to return breaks the chain. The next .then() receives undefined immediately.
Error Propagation in Chains
step1() .then(result1 => { console.log('Step 1 done'); if (result1.invalid) { throw new Error('Invalid result'); } return step2(result1); }) .then(result2 => { console.log('Step 2 done'); // Skipped if error above return step3(result2); }) .then(result3 => { console.log('Step 3 done'); // Skipped if error above }) .catch(error => { console.error('Failed at some step:', error); }); // Errors skip to the next .catch(), skipping intermediate .then()s
When an error occurs, it skips all remaining .then() handlers until a .catch() is found.
Common Mistakes
- Forgetting to return a value/Promise in .then()
- Creating nested Promises inside .then() instead of returning
- Not handling errors that may occur in any step
- Confusing sequential chains with parallel execution
Interview Tips
- Always return in .then() to continue the chain
- Know that returning a Promise waits for it
- Understand error propagation through chains
- Be able to refactor nested callbacks to Promise chains