Async Patternshigh

Async Error Handling

Errors in async code must be explicitly caught. Promise rejections without a .catch handler generate an unhandledRejection warning (and crash in Node 15+). In async/await, use try/catch blocks.

Memory anchor

Async error handling = a safety net under a trapeze artist. No net (no .catch or try/catch) means the performer (rejection) falls and crashes the whole show. In Node 15+, unhandled rejections literally kill the process — the show stops.

Expected depth

A common pattern is wrapping each await in its own try/catch for fine-grained handling, or using a helper like async-safe wrappers: const [err, result] = await to(promise) that returns [error, null] or [null, value] similar to Go-style error handling. Always add a top-level .catch() to Promise chains, even if just for logging. In Express/Koa, unhandled async errors won't reach error middleware unless explicitly passed to next(err) — a frequent source of silent failures.

Deep — senior internals

The unhandledRejection event fires when a rejection has no handler at the end of the current microtask queue drain. A rejection CAN be retroactively handled — if you call .catch() on a rejected promise after the fact, the unhandledRejection event fires but can be 'handled' by the rejectionHandled event. However, relying on this timing is fragile. In Node.js, using --unhandled-rejections=throw (default) or process.on('unhandledRejection') as a global safety net is essential for production. For structured error handling in complex systems, consider result types via libraries like neverthrow or creating discriminated union types with TypeScript.

🎤Interview-ready answer

Async errors must be explicitly caught — unhandled rejections crash Node.js processes in modern versions. try/catch works with async/await for centralized handling; .catch() chains work for Promise chains. The most insidious bug is a missing await before a Promise-returning call — the error lands in an unhandled rejection instead of the surrounding try/catch.

Common trap

Returning a Promise from inside a try block without awaiting it means the try/catch will NOT catch its rejection — the rejection escapes to the outer scope. Always await Promises inside try blocks if you want their errors caught: try { await fetchData(); } not try { return fetchData(); }.

Related concepts