Promise Chaining: A Deep Dive in JavaScript
Chaining .then calls, returning values vs promises, and passing data through the chain.
Promise Chaining: A Deep Dive in JavaScript
Promise chaining lets you sequence async operations flatly. Here is a deep dive into how it works.
How Chaining Works
Each .then (and .catch/.finally) returns a **new promise**. The next .then` waits for it.
fetchP("/api") .then((res) => res.json()) .then((data) => process(data)) .then((result) => render(result)); `` ### Returning a Value If a `.then` callback returns a value, the next `.then` receives it: ```js Promise.resolve(5) .then((n) => n + 1) // 6 .then((n) => n * 2) // 12 .then((n) => console.log(n)); // 12 `` ### Returning a Promise If a `.then` callback returns a promise, the next `.then` waits for it: ```js Promise.resolve(1) .then((n) => fetchP(`/api/${n}`)) .then((res) => res.json()) .then((data) => console.log(data)); `` ### Returning Nothing If a `.then` callback returns nothing (or `undefined`), the next `.then` receives `undefined`: ```js Promise.resolve(5) .then((n) => { console.log(n); }) // returns undefined .then((n) => console.log(n)); // undefined `` ### Passing Multiple Values Promises resolve with a single value. To pass multiple values, use an object or array: ```js fetchUserP(id) .then((user) => fetchPostsP(user).then((posts) => ({ user, posts }))) .then(({ user, posts }) => fetchCommentsP(posts[0]).then((comments) => ({ user, posts, comments }))) .then(({ user, posts, comments }) => render(user, posts, comments)); `` Or use async/await (cleaner): ```js async function main() { const user = await fetchUserP(id); const posts = await fetchPostsP(user); const comments = await fetchCommentsP(posts[0]); render(user, posts, comments); } `` ### Common Pitfall: Not Returning ```js // bad: no return (next .then receives undefined) Promise.resolve(5) .then((n) => { n + 1; }) .then((n) => console.log(n)); // undefined // good: return Promise.resolve(5) .then((n) => n + 1) .then((n) => console.log(n)); // 6 `` ### Common Pitfall: Breaking the Chain ```js // bad: both .then run on the same promise (parallel, not sequential) const p = fetchUserP(id); p.then((user) => fetchPostsP(user)); p.then((user) => fetchCommentsP(user)); // good: chain fetchUserP(id) .then((user) => fetchPostsP(user)) .then((posts) => fetchCommentsP(posts[0])); `` ### The Takeaway Promise chaining: each `.then` returns a new promise. Return a value to pass it to the next `.then`. Return a promise to make the next `.then` wait. Not returning passes `undefined`. For multiple values, use an object/array or async/await. Avoid breaking the chain and nesting.
Each .then returns a new promise. If the callback returns a value, the new promise fulfills with it. If it returns a promise, the new promise waits for it. The next .then receives the result. This lets you sequence async operations flatly.
The next .then receives that value. For example, Promise.resolve(5).then(n => n + 1).then(n => console.log(n)) logs 6. Each value is passed to the next .then.
The next .then waits for the returned promise to settle. The next .then receives the resolved value. This is how you chain async operations that depend on each other.
Return an object or array from each .then: .then(user => fetchPosts(user).then(posts => ({ user, posts }))). Or use async/await with local variables, which is cleaner and avoids this problem.
The next .then receives undefined. The chain continues but with undefined. Arrow functions with block bodies need an explicit return; expression bodies return implicitly.
Ready to master React completely?
Want to upskill yourself, crack your next interview, and get your dream job? Join our comprehensive course to dive deeper with high-quality video tutorials, solve interview questions, and a premium community.
Master React
Want to upskill yourself, crack your next interview, and get your dream job? Join our comprehensive course.

