Glossary / Promise error propagation
Promise error propagation
Promise failures propagate through a chain as rejections until a handler recovers or the rejection reaches the host as unhandled. A .then(onFulfilled) without a rejection handler passes rejection through. A thrown exception in a handler becomes rejection of the Promise returned by that handler. await rethrows a rejection at the await expression, where ordinary try/catch can handle it.
Catch, recover, or rethrow
try {
const data = await loadData();
return transform(data);
} catch (cause) {
throw new Error("Unable to build view model", { cause });
}
A catch handler that returns a value recovers the chain and fulfills it. A catch that logs and forgets to throw can silently turn failure into undefined; subsequent .then handlers then execute. Use finally for cleanup, but remember a throw/rejected Promise from finally can replace the original error. Promise.all rejects on the first observed rejection, but it does not cancel remaining operations.
Attach rejection handling at the boundary that can recover, translate, or report. Avoid both swallowing errors and logging the same rejection at every layer. Detached Promises (for example doWork(); without await/return) need explicit error handling and lifecycle ownership.
Interview questions
- What does await do with a rejected Promise? It throws its rejection reason at the await expression.
- Does a catch handler always propagate the error? No; returning normally recovers the chain.
- Can finally override a rejection? Yes, by throwing or returning a rejected Promise.
- Does Promise.all cancel siblings after one rejects? No.