Error Handling
В одной фразе: правильная обработка ошибок — иерархия кастомных классов с именованными кодами, Error.cause для chaining (ES2022), и явная стратегия разделения операционных ошибок от программных. На собесе проверяют как не “потерять” ошибку в async-коде и как правильно типизировать.
// Иерархия кастомных ошибок — стандарт production-кодаclass AppError extends Error { constructor(message, options = {}) { super(message, { cause: options.cause }); // ES2022: Error.cause this.name = this.constructor.name; // 'AppError', не 'Error' this.code = options.code ?? 'UNKNOWN'; }}class NetworkError extends AppError { constructor(message, { statusCode, ...opts } = {}) { super(message, opts); this.statusCode = statusCode; }}class ValidationError extends AppError { constructor(message, { field, ...opts } = {}) { super(message, opts); this.field = field; }}// Error chaining: сохраняем первопричину через Error.causeasync function fetchUser(id) { try { const resp = await fetch(`/api/users/${id}`); if (!resp.ok) throw new NetworkError('HTTP error', { statusCode: resp.status }); return await resp.json(); } catch (e) { if (e instanceof NetworkError) throw e; // пробрасываем как есть // Оборачиваем, не теряя оригинал: throw new AppError('Failed to fetch user', { cause: e, code: 'FETCH_FAILED' }); }}
// Раскрутка chain:try { await fetchUser(1); } catch (e) { console.log(e.message); // 'Failed to fetch user' console.log(e.cause); // оригинальная ошибка с полным стектрейсом}// Async: unhandledRejection — тихая гибель промиса// ❌ Потерянная ошибкаasync function bad() { const p = riskyOp(); // запустили, не await — ошибка → unhandledRejection return 'done';}
// ✅ Явная обработкаconst p = riskyOp().catch(err => logger.error(err));
// Глобальный перехват (Node.js)process.on('unhandledRejection', (reason) => { logger.fatal(reason); process.exit(1); // рекомендуется: лучше упасть явно});
// В catch всегда проверяй тип и re-throw неизвестное:try { /* ... */ } catch (e) { if (e instanceof ValidationError) return handleValidation(e); throw e; // неизвестные ошибки — не глотать!}Итог: Иерархия кастомных ошибок с name, code и cause делает debugging и мониторинг на порядок эффективнее. Никогда не “глотай” ошибки в пустом catch.