Promises & Promise Chaining
debt(d5/e3/b5/t7)
Closest to 'specialist tool catches it' (d5). ESLint and TypeScript can detect missing .catch() handlers and unhandled rejections in static analysis, but only when explicitly configured with strict rules. The anti-pattern of not returning inner Promises in .then() chains requires code review or runtime testing to catch reliably.
Closest to 'simple parameterised fix' (e3). The quick_fix is straightforward: add .catch() to the chain or wrap in async/await with try/catch. This is typically a localized change within a single function or component, not requiring cross-file refactoring.
Closest to 'persistent productivity tax' (b5). Promise chaining and error handling patterns shape how async code is written throughout a codebase. The choice between callback, Promise chains, async/await, and error handling strategy (catch vs try/catch) creates ongoing cognitive load and inconsistency across multiple work streams, though it doesn't redefine the entire system architecture.
Closest to 'serious trap' (t7). The misconception about Promise.all() failing fast versus Promise.allSettled() waiting for all operations directly contradicts what a developer might expect from similar functions in other languages. Additionally, the Promise constructor anti-pattern (wrapping already-resolved promises) feels intuitive but is inefficient and misunderstands Promise semantics—a trap that contradicts patterns in callback or async/await approaches.
Also Known As
TL;DR
Explanation
A Promise is an object with three states: pending, fulfilled (resolved with a value), or rejected (failed with a reason). .then(onFulfilled, onRejected) returns a new Promise, enabling chains: fetch(url).then(r => r.json()).then(data => process(data)).catch(err => handle(err)). Key behaviours: returning a value from .then() wraps it in a resolved Promise; throwing propagates as rejection; returning another Promise flattens the chain. Static helpers: Promise.all() (all or fail), Promise.allSettled() (all regardless of outcome), Promise.race() (first to settle wins), Promise.any() (first to succeed). Promises replaced callback hell and underpin async/await syntax.
Diagram
flowchart LR
PEND[Pending] -->|resolve| FULFIL[Fulfilled<br/>.then runs]
PEND -->|reject| REJECT[Rejected<br/>.catch runs]
FULFIL --> CHAIN[Chain<br/>.then returns new Promise]
subgraph async/await sugar
ASYNC[async function] -->|awaits| PROM[Promise]
PROM -->|resolved| RESUME[resumes after await]
PROM -->|rejected| THROW[throws - use try/catch]
end
style FULFIL fill:#238636,color:#fff
style REJECT fill:#f85149,color:#fff
style PEND fill:#d29922,color:#fff
Common Misconception
Why It Matters
Common Mistakes
- Missing .catch() on a Promise chain — unhandled rejections crash Node.js and are silently swallowed in browsers.
- Creating a new Promise wrapping another Promise unnecessarily — the Promise constructor anti-pattern.
- Not returning the inner Promise in a .then() callback — the chain breaks and errors escape.
- Using Promise.all() when one failure should not cancel the others — use Promise.allSettled() instead.
Code Examples
// Promise chain with missing return — error escapes:
fetchUser(id)
.then(user => {
fetchOrders(user.id); // Missing return! Chain doesn't wait for this
})
.then(orders => console.log(orders)) // orders is undefined
.catch(err => console.error(err)); // Errors from fetchOrders not caught
// Promise.all — parallel, fails if any rejects
const [user, orders, prefs] = await Promise.all([
fetchUser(id),
fetchOrders(id),
fetchPreferences(id),
]);
// Promise.allSettled — parallel, never throws, check each result
const results = await Promise.allSettled([fetchA(), fetchB()]);
results.forEach(r => {
if (r.status === 'fulfilled') console.log(r.value);
else console.error(r.reason);
});
// Promise.race — first to settle wins (timeout pattern)
const withTimeout = (promise, ms) => Promise.race([
promise,
new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), ms)),
]);
// Promise.any — first to SUCCEED wins (ignores rejections)
const fastest = await Promise.any([mirror1(), mirror2(), mirror3()]);