Обработка ошибок
Обработка ошибок в JavaScript кажется простой — try/catch знают все, — но именно здесь скрыты самые коварные вопросы интервью. throw не «возвращает» ошибку, а разматывает стек. finally может перебить возврат из try. try/catch не поймает ошибку из колбэка setTimeout, потому что тот выполнится позже на новом стеке. Разница между синхронным и асинхронным сбоем здесь — водораздел между junior и senior.
Тема раскладывается на шесть слоёв: сам объект Error и его подтипы, механику throw и распространения, тройку try/catch/finally, пользовательские классы ошибок, асинхронные ошибки (промисы, async/await, глобальные обработчики) и строгий режим, который превращает молчаливые сбои в явные исключения.
Карта темы
- Объект Error и подтипы —
name,message,stackи встроенныеTypeError/RangeError/SyntaxError/ReferenceError. - Оператор throw — что делает
throw, что можно выбрасывать, распространение вверх по стеку и повторный выброс. - try/catch/finally — роль каждого блока, необязательная привязка
catchи опасная семантикаfinally. - Свои классы ошибок —
class extends Error,super(message),this.nameи опцияcause. - Асинхронные ошибки — почему
try/catchне ловит отложенный колбэк,.catch,awaitиunhandledrejection. - Строгий режим —
'use strict'превращает тихие сбои в ошибки; модули и классы строгие всегда.
Частые ошибки и ловушки
| Ошибка | Последствие |
|---|---|
Ждать, что try/catch поймает throw из setTimeout | Ошибка непойманная — колбэк выполнился на новом стеке |
Ставить return в finally | Молча отбрасывает возврат и ошибку из try/catch |
Оборачивать промис в try/catch без await | Отклонение ускользает — синхронный блок уже завершился |
Забыть super(message) в своём классе ошибки | Не настроены базовый message и стек |
Считать stack стандартным свойством | Оно нестандартное, полагаться на формат нельзя |
Думать, что this в обычном вызове — глобал | В строгом режиме this равен undefined |
Значение для собеседований
Это senior-тема, и вопросы здесь диагностические. За throw-в-setTimeout проверяют, понимаете ли вы событийный цикл; за return-в-finally — знаете ли вы порядок выполнения; за пользовательскими ошибками — владеете ли прототипами и instanceof.
Что обычно проверяют:
- Свойства
Error(name/message/stack), встроенные подтипы и что они наследуютError. - Что
throwразматывает стек до ближайшегоcatch, а не возвращает значение. - Семантику
finally— он выполняется всегда, аreturn/throwвнутри него переопределяют ожидающее значение. - Почему асинхронный сбой не ловится синхронным
try/catchи как его обработать через.catch/await.
Типичный неверный ответ: «try/catch ловит любые ошибки внутри блока». На самом деле он защищает только синхронный стек, активный в момент выполнения. Колбэк таймера, промиса или обработчика события выполнится позже — на новом стеке, где этого try уже нет.