Three ways async errors slip past you
try/catch feels like a safety net, and most of the time it is. But async code
has a few holes where an error falls straight through. Here are the three that
have bitten me.
1. Forgetting to await
If you call an async function without await inside a try, the promise
rejects after the try block has already exited. The catch never runs.
try {
doAsyncThing(); // no await — rejection escapes this try
} catch (err) {
// never reached
}
2. Errors inside a forEach callback
Array.forEach ignores the promise its callback returns. Throw inside it and
the loop shrugs.
items.forEach(async (item) => {
await save(item); // a rejection here goes nowhere
});
3. The unhandled rejection you never see
A rejected promise with no .catch does not crash the program the way a thrown
error does. It logs a warning and moves on, so it is easy to miss in
development.
None of these are exotic. They are the ordinary result of async looking
synchronous while behaving otherwise. When in doubt, follow the promise: if
nobody awaits it, nobody catches it.