Promise Chaining in JavaScript
Chaining .then calls lets you sequence async operations. Here is how it works and common pitfalls.
Promise Chaining in JavaScript
Promise chaining lets you sequence async operations flatly instead of nesting callbacks. Here is how it works.
Basic Chaining
fetchUserP(id) .then((user) => fetchPostsP(user)) .then((posts) => fetchCommentsP(posts[0])) .then((comments) => render(user, posts, comments)); `` Each `.then` returns a new promise. The next `.then` waits for it. ### Returning Values 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 Promises If a `.then` callback returns a promise, the next `.then` waits for it: ```js Promise.resolve(1) .then((n) => fetchP(`/api/${n}`)) // returns a promise .then((data) => data.json()) // waits, then transforms .then((result) => console.log(result)); `` ### Common Pitfall: Not Returning ```js // bad: not returning (the next .then receives undefined) Promise.resolve(5) .then((n) => { n + 1; }) // undefined (no return) .then((n) => console.log(n)); // undefined // good: return Promise.resolve(5) .then((n) => n + 1) // 6 .then((n) => console.log(n)); // 6 `` Arrow functions with block bodies need `return`. Expression bodies (no braces) return implicitly. ### Common Pitfall: Breaking the Chain ```js // bad: the chain breaks (p1 is not chained to p2) const p1 = fetchUserP(id); p1.then((user) => fetchPostsP(user)); p1.then((user) => fetchCommentsP(user)); // both .then run on p1, not on each other // good: chain fetchUserP(id) .then((user) => fetchPostsP(user)) .then((posts) => fetchCommentsP(posts[0])); `` ### Common Pitfall: Nested .then (Mini Callback Hell) ```js // bad: nesting (defeats the purpose of promises) fetchUserP(id).then((user) => { fetchPostsP(user).then((posts) => { fetchCommentsP(posts[0]).then((comments) => { render(user, posts, comments); }); }); }); // good: chain flatly 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 (cleanest). ### The Takeaway Promise chaining sequences async operations flatly. Each `.then` returns a new promise. Return values or promises to pass them to the next `.then`. Common pitfalls: not returning (next receives undefined), breaking the chain (parallel instead of sequential), and nesting (defeats the purpose). Use async/await for the cleanest code.
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 undefined. The chain continues but with undefined as the value. Arrow functions with block bodies need an explicit return; expression bodies (no braces) return implicitly.
Yes. If the callback returns a promise, the next .then waits for it to settle. The next .then receives the resolved value. This is how you chain async operations that depend on each other.
It defeats the purpose of promises (creating mini callback hell). Instead of nesting, chain flatly: fetch().then(data => process(data)).then(result => render(result)). Or use async/await for the cleanest code.
Return an object or array from each .then: .then(user => fetchPosts(user).then(posts => ({ user, posts }))).then(({ user, posts }) => ...). Or use async/await with local variables (much cleaner).
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.

