Facebook Pixel

Promise Chaining Pitfalls in JavaScript

Chaining has pitfalls: not returning, breaking the chain, nesting. Here is how to avoid each.

Promise Chaining Pitfalls in JavaScript

Promise chaining has several common pitfalls. Here is each one and how to avoid it.

Pitfall 1: Not Returning

// bad: next .then receives undefined promise.then((data) => { process(data); }); // good: return promise.then((data) => process(data)); `` ### Pitfall 2: 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])); `` ### Pitfall 3: Nesting (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 (or use async/await) `` ### Pitfall 4: Forgetting .catch ```js // bad: unhandled rejection fetchP("/api").then((data) => render(data)); // good: .catch fetchP("/api").then((data) => render(data)).catch((err) => handleError(err)); `` ### Pitfall 5: Sequential Instead of Parallel ```js // slow: sequential const a = await fetchA(); const b = await fetchB(); // fast: parallel const [a, b] = await Promise.all([fetchA(), fetchB()]); `` ### Pitfall 6: Wrapping Promises in new Promise ```js // bad: unnecessary function fetchUser(id) { return new Promise((resolve, reject) => { fetch(`/api/${id}`).then(resolve).catch(reject); }); } // good: just return the promise function fetchUser(id) { return fetch(`/api/${id}`); } `` ### Pitfall 7: Not Handling .catch's Recovery ```js // after .catch, the chain continues as fulfilled fetchP("/api") .catch((err) => "fallback") // recovers .then((result) => render(result)); // receives "fallback", not the error `` If you do not want recovery, re-throw: ```js .catch((err) => { handleError(err); throw err; }) `` ### The Takeaway Promise chaining pitfalls: not returning (undefined flows), breaking the chain (parallel not sequential), nesting (mini callback hell), forgetting .catch (unhandled rejections), sequential instead of parallel (slow), wrapping promises in new Promise (unnecessary), and not understanding .catch's recovery (chain continues as fulfilled).

Not returning from .then (undefined flows to next), breaking the chain (parallel not sequential), nesting .then (mini callback hell), forgetting .catch (unhandled rejections), sequential instead of parallel await (slow), and wrapping promises in unnecessary new Promise.

Chain .then calls directly: fetchUser(id).then(user => fetchPosts(user)).then(posts => ...). Do not store the promise in a variable and call .then multiple times on it (that runs them in parallel, not sequentially).

It defeats the purpose of promises (creating mini callback hell). Instead of nesting, chain flatly. Or use async/await, which is the cleanest way to sequence async operations.

The chain continues as fulfilled with the value returned by .catch. If .catch returns 'fallback', the next .then receives 'fallback'. If .catch throws or re-throws, the next .catch catches it.

Because it is unnecessary. If you already have a promise (from fetch, for example), just return it or chain .then. Wrapping it in new Promise and manually calling resolve/reject is redundant and can introduce bugs.

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.

Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.