Обработка ошибок
try/catch/finally, выбрасывание ошибок, объект Error и его подтипы, пользовательские ошибки, строгий режим и обработка асинхронных ошибок.
12 вопросов
JuniorТеорияОчень частоКакие свойства есть у объекта Error и какие встроенные подтипы существуют?
Какие свойства есть у объекта Error и какие встроенные подтипы существуют?
Объект Error содержит name (тип, например 'Error'), message (человекочитаемый текст, переданный конструктору) и нестандартное, но широко поддерживаемое stack — строку стека вызовов. Встроенные подтипы наследуются от Error и задают свой name: TypeError, RangeError, SyntaxError, ReferenceError, а также URIError и EvalError.
Типичные ошибки
- ✗Считать
stackстандартным свойством, гарантированным на каждом движке - ✗Не знать, что встроенные подтипы наследуют
Error, поэтомуinstanceof Errorдаётtrue - ✗Путать
name(тип) сmessage(человекочитаемый текст)
Уточняющие вопросы
- →Какая ошибка выбрасывает
ReferenceError, а какая —TypeError? - →Почему
SyntaxErrorиз плохого исходного кода обычно невозможно поймать черезcatch?
JuniorТеорияОчень частоЧто делает каждый из блоков try, catch и finally в JavaScript?
Что делает каждый из блоков try, catch и finally в JavaScript?
try оборачивает код, который может выбросить ошибку. Если он выбрасывает, управление переходит в catch, получающий выброшенное значение как привязку. finally выполняется потом независимо от того, была ли ошибка, даже после return, поэтому используется для очистки. С ES2019 привязка в catch необязательна: catch {} допустим, когда ошибка игнорируется.
Типичные ошибки
- ✗Думать, что
finallyпропускается, когда блокtryделаетreturnили выбрасывает - ✗Считать привязку в
catchобязательной, не зная, чтоcatch {}допустим с ES2019 - ✗Полагать, что
catchвыполняется даже при успешномtryбез ошибки
Уточняющие вопросы
- →Если и
try, иfinallyвозвращают значение, чейreturnпобеждает? - →Когда вы использовали бы
try/finallyвообще безcatch?
JuniorТеорияЧастоЧто такое 'use strict' и что включает строгий режим?
Что такое 'use strict' и что включает строгий режим?
'use strict' — строковая директива в начале скрипта или функции, переводящая эту область в строгий режим. Движок начинает отвергать небрежные конструкции: присваивание необъявленной переменной выбрасывает ошибку вместо создания глобала, дублирующиеся имена параметров запрещены, а this равен undefined при обычном вызове функции. ES-модули и тела классов всегда строгие, поэтому директива нужна лишь в устаревших скриптах.
Типичные ошибки
- ✗Думать, что строгий режим добавляет статическую проверку типов в JavaScript
- ✗Не знать, что ES-модули и тела классов строгие по умолчанию
- ✗Считать, что директива применяется глобально, а не к скрипту или функции
Уточняющие вопросы
- →Почему директива
'use strict'должна быть самым первым оператором в области? - →Как строгий режим меняет значение
thisпри автономном вызове функции?
MiddleТеорияЧастоКак обрабатывать ошибки из промисов и кода с async/await?
Как обрабатывать ошибки из промисов и кода с async/await?
Отклонённый промис обрабатывают через .catch(handler), прицепленный после .then. С async/await оберните await в try/catch, ведь отклонение превращается в выброшенную ошибку в точке await. Если ни того, ни другого нет, отклонение необработано: браузер вызывает глобальное событие unhandledrejection, а Node пишет предупреждение. try/catch не ловит отклонение, которое вы не await.
Типичные ошибки
- ✗Оборачивать промис в
try/catchбезawait, из-за чего отклонение ускользает - ✗Забыть
.catchв цепочке промисов, оставляя отклонение необработанным - ✗Не знать, что браузер вызывает
unhandledrejectionдля пропущенных отклонений
Уточняющие вопросы
- →Почему синхронный
try/catchне ловит отклонение от не-await-нутого промиса? - →Что позволяет событие
unhandledrejectionи можно ли отменить поведение по умолчанию?
JuniorТеорияИногдаЧто делает оператор throw и какие значения можно выбрасывать?
Что делает оператор throw и какие значения можно выбрасывать?
throw expr немедленно останавливает текущую функцию и начинает разматывать стек в поисках catch. Выбросить можно ЛЮБОЕ значение — строку, число или объект, — но следует выбрасывать экземпляр Error, ведь только они несут message и stack для диагностики. Внутри catch вызов throw err повторно выбрасывает ту же ошибку, передавая её внешнему обработчику.
Типичные ошибки
- ✗Думать, что выбросить можно только экземпляры
Error, хотя допустимо любое значение - ✗Считать, что
throwвозвращается к вызывающему, а не разматывает стек - ✗Полагать, что повторный выброс
throw errперестраивает стек с места повторного выброса
Уточняющие вопросы
- →Почему на практике не рекомендуют выбрасывать обычную строку вместо
Error? - →Как повторный выброс внутри
catchсохраняет исходную трассировку стека?
MiddleТеорияИногдаКак определить пользовательский класс ошибки и что добавляет опция cause?
Как определить пользовательский класс ошибки и что добавляет опция cause?
Напишите class MyError extends Error и вызовите super(message) в конструкторе, затем задайте this.name = 'MyError', чтобы тип отображался верно. Экземпляры проходят и instanceof MyError, и instanceof Error, позволяя обработчикам ветвиться по типу. Опция cause из ES2022 — new Error('msg', { cause: original }) — прикрепляет исходную ошибку как err.cause, сохраняя оригинал при оборачивании и повторном выбросе.
Типичные ошибки
- ✗Забыть
super(message), из-за чего базовыеmessageи стек не настраиваются - ✗Не задать
this.name, оставляя ошибку с типом обычногоError - ✗Думать, что
causeперезаписываетmessage, а не прикрепляет исходную ошибку
Уточняющие вопросы
- →Почему пользовательской ошибке стоит задавать
this.name, а не полагаться на имя класса? - →Как
err.causeпомогает при оборачивании низкоуровневой ошибки в предметную?
MiddleТеорияИногдаКак непойманная ошибка распространяется вверх по стеку вызовов до обработки?
Как непойманная ошибка распространяется вверх по стеку вызовов до обработки?
Когда функция выбрасывает и не имеет локального catch, движок разматывает стек: он покидает эту функцию и каждого вызывающего по очереди, ища ближайший объемлющий try/catch. Первый подходящий catch вверх по цепочке обрабатывает ошибку; код после каждого покинутого вызова пропускается. Если обработчик не найден вовсе, ошибка достигает вершины и становится непойманным исключением. Поэтому ловят там, где можно осмысленно восстановиться, а не обязательно в месте выброса.
Типичные ошибки
- ✗Думать, что ошибка распространяется в вызываемые функции, а не вверх к вызывающим
- ✗Считать, что распространение останавливается на непосредственном вызывающем
- ✗Полагать, что ошибка попадает в самый внешний
try, а не в ближайший объемлющий
Уточняющие вопросы
- →Почему ловить ошибку в месте выброса часто хуже, чем дать ей распространиться?
- →Что происходит с кодом после покинутого вызова функции во время разматывания?
MiddleТеорияИногдаКогда выполняется finally и что происходит, если в нём есть return или throw?
Когда выполняется finally и что происходит, если в нём есть return или throw?
finally выполняется после завершения try/catch на любом пути — обычном выходе, return или непойманном выбросе, — что делает его идеальным для очистки. Но return или throw внутри finally переопределяет то, что try или catch собирались вернуть или выбросить: ожидающее значение отбрасывается, и побеждает результат finally. Поэтому управление потоком в finally может молча проглотить ошибку.
Типичные ошибки
- ✗Думать, что
returnвtryпропускает блокfinally - ✗Не знать, что
returnвfinallyотбрасывает возвращаемое значениеtry/catch - ✗Считать, что
throwвfinallyловитсяcatchтого же оператора
Уточняющие вопросы
- →Почему
returnвнутриfinallyсчитают опасным антипаттерном? - →Если
tryвыбрасывает иfinallyтоже выбрасывает, какая ошибка доходит до вызывающего?
MiddleТеорияИногдаКакие молчаливые сбои строгий режим превращает в выброшенные ошибки?
Какие молчаливые сбои строгий режим превращает в выброшенные ошибки?
Строгий режим превращает несколько тихих небрежных поведений в ошибки: присваивание необъявленной переменной выбрасывает ReferenceError вместо создания неявного глобала; запись в read-only или незаписываемое свойство выбрасывает TypeError, а не молча проваливается; а delete неконфигурируемого свойства выбрасывает. Также this равен undefined при обычном вызове функции, поэтому случайные глобалы через this отлавливаются.
Типичные ошибки
- ✗Думать, что строгий режим лишь предупреждает, а не выбрасывает на этих сбоях
- ✗Полагать, что
thisвсё ещё глобальный объект при обычном вызове в строгом режиме - ✗Считать, что присваивание необъявленному всё ещё создаёт неявный глобал
Уточняющие вопросы
- →Почему правило строгого режима
thisравенundefinedзащищает от случайных глобалов? - →Какой
TypeErrorвыбрасывает запись в свойство замороженного объекта в строгом режиме?
SeniorТеорияИногдаПочему throw внутри setTimeout ускользает от объемлющего try/catch?
Почему throw внутри setTimeout ускользает от объемлющего try/catch?
try/catch защищает лишь синхронный стек вызовов, активный в момент выполнения. Колбэк setTimeout срабатывает позже на свежем стеке через цикл событий, уже после возврата из блока try, поэтому его выброс не имеет объемлющего обработчика и становится непойманным. Промисы это решают: выброс внутри тела then/async отклоняет промис, ловимый через .catch или await в try/catch. С Promise.all первое отклонение сразу отклоняет весь комбинатор.
Типичные ошибки
- ✗Думать, что
try/catchзащищает колбэки, выполняемые позже на новом стеке - ✗Считать, что выброс внутри тела
asyncилиthenпроглатывается, а не отклоняет - ✗Полагать, что
Promise.allждёт все отклонения, а не отклоняется на первом
Уточняющие вопросы
- →Как на практике обработать ошибку, выброшенную внутри колбэка
setTimeout? - →Чем
Promise.allSettledотличается отPromise.allв обработке отклонений?
SeniorТеорияИногдаКак сочетаются цепочки error.cause, AggregateError и глобальные обработчики ошибок?
Как сочетаются цепочки error.cause, AggregateError и глобальные обработчики ошибок?
error.cause позволяет обернуть низкоуровневую ошибку в предметную, сохраняя оригинал — обработчики проходят по цепочке cause до корневого сбоя. AggregateError объединяет несколько ошибок в одну (массив .errors), как делает Promise.any, когда все входы отклонены. В крайнем случае глобальные обработчики — window.onerror и событие unhandledrejection в браузере или process.on('uncaughtException') в Node — ловят то, что ускользнуло от всех try/catch.
Типичные ошибки
- ✗Думать, что
causeперезаписывает сообщение, а не связывает исходную ошибку - ✗Считать, что
AggregateErrorхранит одну ошибку, а не массив.errorsиз всех - ✗Полагать, что глобальный обработчик опережает локальный
try/catch, а не крайний случай
Уточняющие вопросы
- →Какой комбинатор промисов выдаёт
AggregateErrorи при каком условии? - →Почему
window.onerror— инструмент мониторинга, а не настоящий механизм восстановления?
SeniorТеорияИногдаКаковы полные эффекты строгого режима и где он всегда включён?
Каковы полные эффекты строгого режима и где он всегда включён?
Строгий режим запрещает неявные глобалы, выбрасывает при записи в read-only или несуществующее свойство, запрещает дублирующиеся имена параметров и with, делает this равным undefined при обычных вызовах и резервирует слова вроде implements и interface. Важно: ES-модули и тела классов строгие автоматически — директива там не нужна. Поэтому присваивание свойству замороженного объекта выбрасывает TypeError внутри модуля, тогда как небрежные скрипты молча провалились бы.
Типичные ошибки
- ✗Думать, что ES-модули и тела классов требуют явного
'use strict' - ✗Считать, что запись в замороженное свойство молча проваливается, а не выбрасывает
- ✗Полагать, что строгий режим лишь предупреждает, а не выбрасывает настоящие
TypeError
Уточняющие вопросы
- →Почему присваивание свойству замороженного объекта выбрасывает только в строгом режиме?
- →Как строгий режим по умолчанию в модулях меняет написание библиотечного кода?