How do you handle errors across callbacks, promises, and async/await?
3 minintermediatenodejserror-handlingasynctry-catchpromises
Quick Answer
Callbacks: check the error-first argument. Promises: attach .catch() (and know Promise.all is fail-fast). async/await: wrap awaits in try/catch. The key is that a try/catch only catches awaited/synchronous errors — it won't catch errors from un-awaited promises or from callbacks that reject out-of-band.
Detailed Answer
Answer: Each async style has its own error channel; mixing them incorrectly is where bugs hide.
Callbacks — error-first argument:
fs.readFile('f.txt', (err, data) => {
if (err) return handle(err);
use(data);
});
Promises — .catch:
doWork()
.then(use)
.catch(handle); // catches rejections from the whole chain
async/await — try/catch:
try {
const data = await readFile('f.txt');
use(data);
} catch (err) {
handle(err);
}
Critical subtleties:
- A
try/catcharoundawaitonly catches the awaited promise. If you forgetawait, the error escapes as an unhandled rejection:
try {
doAsync(); // ❌ no await — a rejection here is NOT caught
} catch (e) {}
- Errors thrown inside a callback passed to something else aren't caught by an enclosing try/catch:
try {
setTimeout(() => { throw new Error('boom'); }, 0); // ❌ escapes — crashes process
} catch (e) {}
- In Express, synchronous throws in a handler are caught by Express, but rejected promises are not (pre-Express 5) — you must call
next(err)or use an async wrapper.
Rule: match the handler to the async mechanism, and always await (or .catch) every promise.