Can
try/catchcatch an error from an asynchronous callback?JavaScriptconst log = []; function loadLater(callback) { setTimeout(() => callback(new Error('late')), 0); } try { loadLater((err) => { log.push('callback: ' + err.message); console.log(log.join(', ')); }); log.push('try done'); } catch (err) { log.push('caught'); }try/catchcovers only code that runs synchronously inside it. When the timer fires, thetryhas finished, so the callback's error never reaches thecatch. That is why callback APIs pass errors as an argument. With promises,awaitbrings the error back into thetry.How do you handle errors with async/await in Node.js?
Wrap the
awaitintry/catch: a rejected promise throws at theawait. Put cleanup infinally.JavaScriptasync function fetchUser(id) { if (id <= 0) throw new Error(`invalid id ${id}`); return { id, name: 'Ada' }; } async function handler(id) { try { const user = await fetchUser(id); return `found ${user.name}`; } catch (err) { return `failed: ${err.message}`; } finally { console.log(`request ${id} finished`); } } async function main() { console.log(await handler(7)); console.log(await handler(-1)); } main();finallyruns before the promise settles, sorequest 7 finishedprints beforefound Ada. Catch only where you can act; let the rest reach a central handler. See error handling in async code.Is
returnthe same asreturn awaitinsidetry/catch?JavaScriptasync function fail() { throw new Error('boom'); } async function withoutAwait() { try { return fail(); } catch { return 'caught'; } } async function withAwait() { try { return await fail(); } catch { return 'caught'; } } async function main() { const a = await withoutAwait().catch((e) => 'rejected: ' + e.message); const b = await withAwait(); console.log(a, '|', b); } main();return fail()returns the rejected promise without waiting, so the function leaves thetrybefore it rejects.return awaitrejects inside thetry, so it is caught. Insidetry/catch, alwaysreturn await.What happens to an unhandled promise rejection in Node.js?
Since Node 15 the process crashes with exit code 1, like an uncaught exception; before 15 it only warned.
process.on('unhandledRejection', (reason) => { console.error('unhandled rejection:', reason); process.exitCode = 1; // log, flush, then shut down; do not carry on as if nothing happened }); async function save() { throw new Error('db down'); } save(); // no await, no .catch(): this is the bugThe handler is for logging and shutdown. The fix is at the call site:
awaitthe promise or add.catch().Should you use
process.on('uncaughtException')to keep a server running?No. Log and exit: after an uncaught exception the state is unknown (a half-done transaction, a held lock), and Node's documentation says resuming is unsafe.
process.on('uncaughtException', (err) => { console.error('fatal:', err); process.exit(1); // the process manager starts a clean instance });Let a supervisor (Kubernetes, systemd, PM2) restart it, and run several instances.
What happens when an emitter emits
'error'with no listener?JavaScriptconst EventEmitter = require('node:events'); const job = new EventEmitter(); let result; try { job.emit('error', new Error('disk full')); result = 'emitted'; } catch (err) { result = 'thrown: ' + err.message; } console.log(result);With no
'error'listener,emit('error')throws; other events with no listener are ignored. A missingstream.on('error', ...)is a common production crash, so attach one or usepipeline.What is the difference between operational errors and programmer errors?
Operational errors are expected failures of a correct program: the database is down, input is invalid, a request timed out. Handle them: retry, return a 4xx or 5xx. Programmer errors are bugs, such as reading a property of
undefined; log, crash, restart and fix the code. Branch onerr.code(ENOENT,ECONNRESET), never on messages.How do you create custom error classes in Node.js?
Extend
Error, setname, add fields such asstatus, and pass the original error ascause(Node 16.9 and later).JavaScriptclass AppError extends Error { constructor(message, { status = 500, cause } = {}) { super(message, { cause }); this.name = this.constructor.name; this.status = status; } } class NotFoundError extends AppError { constructor(what, options) { super(`${what} not found`, { status: 404, ...options }); } } try { try { JSON.parse('{bad json'); } catch (err) { throw new NotFoundError('config', { cause: err }); } } catch (err) { console.log(err.name, err.status, err.message); console.log(err instanceof AppError, err.cause instanceof SyntaxError); }Handlers branch on
instanceoforstatus, not on message text. See error types.How do you add a timeout to an async operation?
Race the operation against a rejecting timer, and clear the timer afterwards.
JavaScriptfunction withTimeout(promise, ms) { let timer; const timeout = new Promise((_, reject) => { timer = setTimeout(() => reject(new Error(`timed out after ${ms} ms`)), ms); }); return Promise.race([promise, timeout]).finally(() => clearTimeout(timer)); } const sleep = (ms, value) => new Promise((resolve) => setTimeout(() => resolve(value), ms)); async function main() { console.log(await withTimeout(sleep(10, 'fast result'), 100)); try { await withTimeout(sleep(200, 'slow result'), 50); } catch (err) { console.log(err.message); } } main();Promise.racestops the waiting, not the work. Where cancellation is supported, pass a signal:fetch(url, { signal: AbortSignal.timeout(5000) }).Does the second argument of
.thencatch an error thrown in its first argument?JavaScriptconst log = []; Promise.resolve() .then( () => { throw new Error('in then'); }, () => log.push('second argument'), ) .catch((err) => log.push('catch: ' + err.message)) .then(() => console.log(log.join(', ')));It prints
catch: in then. The second argument of.thenhandles a rejection of the promise before it, not an error thrown by the first argument. That error rejects the promise.thenreturns, so it skips ahead to the next.catch. End a chain with one.catchinstead of passing two callbacks.
Node.js Error Handling Interview Questions
Where an error goes in callbacks, promises, emitters and the process, and what a service should do with it.