Suggest an editImprove this articleRefine the answer for “What is promise chaining in JavaScript”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**Promise chaining** is a pattern where each `.then()` returns a new Promise, passing its resolved value to the next handler in the sequence. ```javascript fetch('/api/user') .then(res => res.json()) .then(user => user.name) .then(name => console.log(name)) .catch(err => console.error(err)); ``` **Key point:** forget `return` inside `.then()` and the next handler gets `undefined`.Shown above the full answer for quick recall.Answer (EN)Image**Promise chaining** is a pattern where each `.then()` returns a new Promise, so the next handler in the chain receives the previous one's result as input. ## Theory ### TL;DR - Think of it like dominoes: knock over the first, each one triggers the next automatically. - Each `.then()` creates a fresh Promise resolved with whatever your handler returns. - Errors bubble down the chain to the nearest `.catch()` - you need just one for the whole flow. - Use chaining for linear async steps (fetch, parse, process). For loops or conditional branches, [`async/await`](/questions/what-is-async-await-in-javascript) reads better. - `async/await` is syntactic sugar over promise chaining, not a replacement. ### Quick example ```javascript fetch('https://jsonplaceholder.typicode.com/users/1') .then(response => response.json()) // returns Promise resolved with user object .then(user => `Hello, ${user.name}!`) // returns Promise resolved with string .then(greeting => console.log(greeting)) // logs: "Hello, Leanne Graham!" .catch(err => console.error(err)); // catches any error in the chain ``` Each `.then()` passes its return value to the next handler. If any step fails, execution skips all remaining `.then()` calls and jumps straight to `.catch()`. ### How the chain works internally When you call `.then(handler)`, JavaScript creates a new Promise and schedules `handler` on the microtask queue. When the previous Promise settles, the engine dequeues the job, runs your handler, and resolves the new Promise with whatever the handler returns. If the handler returns a plain value, it gets wrapped in a resolved Promise automatically. If it returns another Promise, the chain waits for that inner Promise to settle before continuing. If it throws, the new Promise rejects. All of this runs in the microtask queue, which the [event loop](/questions/what-is-event-loop-in-javascript) drains before any `setTimeout` or `setInterval` callbacks run. A 5-step chain with sync returns completes entirely before any timer fires. ### Key difference from nested callbacks Without chaining, you nest. That is the core problem. ```javascript // nested - callback hell fetch('/user').then(response => { response.json().then(user => { fetch(`/posts/${user.id}`).then(res => { res.json().then(posts => console.log(posts)); }); }); }); // chained - flat and readable fetch('/user') .then(res => res.json()) .then(user => fetch(`/posts/${user.id}`)) .then(res => res.json()) .then(posts => console.log(posts)) .catch(err => console.error(err)); ``` Same logic, completely different readability. The chained version also covers all errors with a single `.catch()`. ### When to use - 2-4 linear async steps (fetch, parse, transform, respond) - chain `.then()`. - One error handler for the whole flow - single `.catch()` at the end. - Mix of sync and async returns in one flow - chaining handles both automatically. - Parallel operations - use [`Promise.all()`](/questions/what-is-promise-all-in-javascript) instead. - Conditional branches or loops - `async/await` is cleaner there. ### Common mistakes **Mistake 1: forgetting to return inside `.then()`** ```javascript // wrong - returns undefined, value is lost fetch('/user') .then(res => res.json()) .then(user => { user.name; }) // no return .then(name => console.log(name)); // logs: undefined // correct fetch('/user') .then(res => res.json()) .then(user => user.name) // implicit return in arrow function .then(name => console.log(name)); // logs: "Leanne Graham" ``` **Mistake 2: mid-chain `.catch()` that swallows errors** ```javascript // wrong - first .catch() resolves the chain with undefined fetch('/user') .catch(err => console.log('error')) // catches but does not rethrow .then(data => console.log(data)); // runs even on failure, logs: undefined // correct - rethrow if you need the error to propagate fetch('/user') .catch(err => { console.log(err); throw err; }) .then(data => console.log(data)); ``` **Mistake 3: nesting `.then()` instead of chaining** ```javascript // wrong - nested, breaks the flat structure fetch('/user').then(res => { return res.json().then(user => fetch(`/posts/${user.id}`)); }); // correct - return the inner promise and let the chain continue fetch('/user') .then(res => res.json()) .then(user => fetch(`/posts/${user.id}`)) .then(res => res.json()); ``` **Mistake 4: not knowing that `.then()` auto-wraps sync values** ```javascript Promise.resolve(5) .then(x => x * 2) // returns Promise<10>, not the number 10 .then(y => console.log(y)); // logs: 10 ``` This is correct behavior, not a bug. But it matters for timing: code outside the chain does not see `10` synchronously, even though the math itself is instant. ### Real-world usage - Express.js: route handlers chain `fetchUser().then(validate).then(respond).catch(errorHandler)`. - React `useEffect`: chain `fetch` calls to load a user and then their posts in order. - Axios interceptors: request, transform, and response as a built-in chain. - Node.js `fs.promises`: `readFile().then(parse).then(writeFile)`. - The mid-chain `.catch()` mistake ships to production quietly. It only surfaces when the first request actually fails in the real environment. ### Follow-up questions **Q:** What happens if a `.then()` handler throws an error? **A:** The Promise returned by that `.then()` rejects with the thrown error. Execution skips all remaining `.then()` handlers and jumps to the next `.catch()` in the chain. **Q:** Can you have multiple `.catch()` calls in one chain? **A:** Yes. But the first `.catch()` consumes the rejection and resolves the chain with its return value, unless you explicitly rethrow with `throw`. Later `.catch()` calls do not trigger without that rethrow. **Q:** What is the difference between returning a plain value vs a Promise inside `.then()`? **A:** Both work from the outside. A plain value gets wrapped in a resolved Promise automatically. A returned Promise makes the chain wait for it to settle. Either way, the next `.then()` receives the resolved value. **Q:** How does promise chaining relate to the event loop? **A:** Each `.then()` schedules a microtask. The event loop processes all microtasks before moving to macrotasks like `setTimeout`. A 5-step chain with sync returns completes entirely before any timer callback runs. **Q:** When would you prefer `async/await` over chaining? **A:** Any time you have conditional logic, loops over async operations, or need `try/catch` style error handling. Chains get hard to read when you add `if` statements inside `.then()`. For a simple linear sequence, chaining works fine and is sometimes shorter. **Q:** (Senior) What does V8 do when a `.then()` handler returns another Promise? **A:** V8 does not pass the returned Promise as the resolved value directly. It runs the Promise resolution procedure: calls `.then()` on the inner Promise and connects its settlement to the outer one. The chain pauses and inherits the inner Promise's resolved value, not the Promise object itself. ## Examples ### Basic: fetch a user by ID ```javascript fetch('https://jsonplaceholder.typicode.com/users/1') .then(response => { if (!response.ok) throw new Error('Not found'); return response.json(); // parse JSON, returns Promise }) .then(user => { console.log(user.name); // logs: "Leanne Graham" return user.id; }) .then(id => console.log(`User ID: ${id}`)) // logs: "User ID: 1" .catch(err => console.error('Failed:', err.message)); ``` Each step passes a value forward. If `response.ok` is false, the thrown error skips the remaining handlers and lands in `.catch()`. ### Intermediate: Express route with chained DB calls ```javascript app.get('/user/:id', (req, res) => { fetchUser(req.params.id) .then(user => fetchUserPosts(user.id)) // fetch related data .then(posts => res.json(posts)) // respond with posts array .catch(err => res.status(500).json({ error: err.message })); }); ``` One `.catch()` covers both DB calls. If `fetchUser` fails, `fetchUserPosts` never runs, and the error response fires immediately. ### Advanced: returning a Promise from inside `.then()` ```javascript function delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } delay(100) .then(() => { return delay(200).then(() => 'done'); // chain pauses for the inner promise }) .then(result => console.log(result)); // logs: "done" after ~300ms total ``` When you return a Promise from `.then()`, the outer chain does not continue until that Promise resolves. The result is the inner Promise's value, not the Promise object itself. This is the same mechanism that makes chained `fetch` calls work correctly.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.