Перейти к основному содержимому

Обработка исключений в JavaScript

Разработчику Архитектору
Сначала — общая теория

Перед этой главой прочитайте Ошибки, исключения и отказоустойчивость — в частности чем ошибка отличается от исключения.

Исключение в JS — предсказуемое отклонение (нет файла, неверный аргумент), которое перехватывают и обрабатывают, чтобы продолжить сценарий.
Фатальный сбой — необработанное исключение или unhandledrejection, из‑за которых падает вкладка, скрипт или (в Node 15+) процесс.
Встроенные типы Error — в 241.


Синтаксис: try / catch / finally

JavaScript использует структурную обработку: блок try связывается с catch и опциональным finally.

function readConfig(path) {
let raw;
try {
raw = fs.readFileSync(path, 'utf8');
return JSON.parse(raw);
} catch (e) {
if (e instanceof SyntaxError) {
throw new Error(`Некорректный JSON в ${path}`, { cause: e });
}
throw e;
} finally {
// освобождение ресурсов, если появятся (дескрипторы, временные файлы)
}
}
КонструкцияНазначение
tryКод, где сбой возможен
catch (e)Обработчик; в TS предпочтительно unknown + сужение типа
finallyВыполняется всегда (очистка)
throw valueПрервать поток; для API лучше throw new Error(...)

Раскрутка стека и поиск обработчика — в общей теории.

Интерактивное демо — сценарии на Python в компоненте ниже; в JS синтаксис try/catch, но стек тот же.

Загрузка демо исключений…

throw и цепочка cause

Всегда бросайте экземпляры Error (или наследников), а не строки — так сохраняются stack и cause.

async function loadUser(id) {
try {
const res = await fetch(`/api/users/${id}`);
if (!res.ok) {
throw new Error(`HTTP ${res.status}`);
}
return await res.json();
} catch (e) {
throw new Error(`Не удалось загрузить пользователя ${id}`, { cause: e });
}
}

В логах и DevTools видна цепочка cause — не теряйте её при обёртке (см. типичные ошибки).


Синхронный код vs Promise и async/await

КонтекстКак ловить
Синхронныйtry/catch вокруг вызова
async функцияtry/catch вокруг await
.then()второй аргумент или .catch()
Ошибка внутри колбэка thenпревращается в rejected Promise
// Правильно: await в try
async function save() {
try {
await db.insert(row);
} catch (e) {
logger.error(e);
throw e;
}
}

// Ошибка: забыли await — rejection уйдёт мимо try
async function broken() {
try {
db.insert(row); // Promise без await
} catch (e) {
/* сюда не попадём при reject */
}
}

Теория асинхронных сбоев — раздел 4.05, FAQ — 998.


unhandledrejection и глобальные обработчики

Необработанный rejected Promise в браузере → событие unhandledrejection. В Node — то же; на современных версиях необработанный rejection может завершить процесс.

// Браузер
window.addEventListener('unhandledrejection', (event) => {
reportToSentry(event.reason);
});

// Node
process.on('unhandledRejection', (reason) => {
console.error('Unhandled rejection:', reason);
});

Синхронные необработанные ошибки — window.onerror / process.on('uncaughtException') (последний шанс перед выходом). Подробнее — 111.


Express и границы API

В async маршрутах Express ошибки из await нужно передать в next(err) или обернуть в middleware — иначе rejection не попадёт в центральный обработчик. См. Express — middleware и ошибки.

На границе HTTP — единый формат ответа, без stack trace клиенту: обработка ошибок в API.


TypeScript

Сужение в catch: catch (e: unknown) и проверка instanceof Error. Доменные коды и ResultОбработка ошибок в TypeScript.


Связанные материалы


Что запомнить

  • Бросайте Error, сохраняйте cause при обёртке.
  • В async коде оборачивайте await в try/catch.
  • Следите за unhandledrejection в продакшене.
  • Типы встроенных ошибок — в 241; контракт API — отдельно от механизма throw.