W3docs

Обработка ошибок в JavaScript с try…catch

Обработка ошибок в JavaScript: try, catch, finally, throw, объект Error, встроенные типы ошибок, повторный выброс и асинхронные ограничения.

Обработка ошибок в JavaScript с помощью Try...Catch

Эффективная обработка ошибок — важнейший аспект создания надёжных приложений на JavaScript. Когда во время выполнения что-то идёт не так — сетевой запрос завершается с ошибкой, JSON оказывается некорректным, переменная равна undefined — JavaScript выбрасывает исключение. Без обработки это исключение останавливает выполнение скрипта. В этой статье рассматриваются инструкция try...catch, блок finally, оператор throw, объект Error и его встроенные подтипы, повторный выброс исключений, а также важное ограничение: синхронный try...catch не может поймать ошибки, выброшенные внутри асинхронных колбэков.

Понимание Try...Catch в JavaScript

Инструкция try...catch — инструмент управления исключениями, то есть ошибками, возникающими во время выполнения программы. Она позволяет обрабатывать исключения корректно, не допуская аварийного завершения всего скрипта.

Механизм состоит из двух блоков:

  • try — код, который может выбросить исключение. JavaScript выполняет его в обычном режиме.
  • catch (error) — выполняется только в том случае, если что-то в блоке try выбросило ошибку. Выброшенное значение передаётся в виде параметра error.

Если ошибки не возникает, блок catch полностью пропускается. Если ошибка возникает, выполнение немедленно переходит в catch — остаток блока try не выполняется.

Базовый синтаксис Try...Catch

Вот простой пример, демонстрирующий базовую структуру try...catch:


javascript— editable

В этом примере любая ошибка, возникшая внутри блока try, перехватывается блоком catch, где она может быть обработана без аварийного завершения скрипта.

Выброс собственных ошибок с помощью throw

Оператор throw генерирует исключение. Можно выбросить любое значение, однако принятое соглашение — и единственное, что следует делать на практике — это выбрасывать объект Error (или экземпляр одного из его подтипов). Это автоматически предоставляет полезные свойства message и трассировку stack.

javascript— editable

Избегайте throw 'a string' или throw 42. У выброшенной строки нет свойств message, name и stack, поэтому код, ожидающий объект Error (как большинство кода), будет работать некорректно. Для предметно-ориентированных ошибок с дополнительными полями определите собственный класс — см. пользовательские ошибки, расширение Error.

Объект Error

При выполнении new Error(message) вы получаете объект с тремя часто используемыми свойствами:

  • name — тип ошибки, например "Error", "TypeError", "SyntaxError".
  • message — понятное человеку описание, переданное в конструктор.
  • stack — строка со стеком вызовов в момент создания ошибки (нестандартное, но поддерживаемое всеми основными движками свойство; полезно для отладки, но не для управления потоком выполнения).
javascript— editable

Встроенные типы ошибок

JavaScript поставляется с несколькими подклассами Error, которые движок выбрасывает автоматически. Знание этих типов помогает писать точную логику обработки в catch:

ТипКогда выбрасывается
SyntaxErrorКод или данные имеют неверный формат, например JSON.parse() при некорректном JSON.
TypeErrorЗначение имеет неожиданный тип, например вызов не-функции или чтение свойства у undefined.
ReferenceErrorОбращение к несуществующей переменной.
RangeErrorЗначение выходит за допустимый диапазон, например недопустимая длина array.
URIErrordecodeURIComponent() или аналогичная функция получила некорректный URI.
EvalErrorИсторическое исключение; редко выбрасывается современными движками.

У каждого из этих типов свойство name соответствует имени типа, поэтому можно делать ветвление по instanceof:

javascript— editable

Обработка конкретных ошибок

Можно также обрабатывать определённые типы ошибок, анализируя объект ошибки:


javascript— editable

Этот пример специально обрабатывает SyntaxError, которая может возникнуть при разборе JSON. Если перехваченная ошибка является экземпляром SyntaxError, она обрабатывается путём вывода конкретного сообщения. В противном случае ошибка повторно выбрасывается — она может быть перехвачена обработчиком более высокого уровня или привести к аварийному завершению программы, сигнализируя о необработанной ошибке.

Повторный выброс ошибок

Блок catch перехватывает все ошибки из своего try, включая те, которые вы не знаете как обработать. Рекомендуемый паттерн: проверить ошибку, обработать понятные случаи и повторно выбросить всё остальное, чтобы оно попало к внешнему обработчику. Молчаливое поглощение неизвестных ошибок скрывает реальные баги.

javascript— editable

Необязательная привязка в catch

Если значение ошибки не нужно, привязку можно опустить полностью (ES2019+). Это удобно, когда важен лишь сам факт сбоя:

javascript— editable

Использование Finally

Блок finally выполняется после блоков try и catch независимо от того, было ли выброшено или перехвачено исключение. Он полезен для освобождения ресурсов или выполнения завершающих операций вне зависимости от результата try...catch:


javascript— editable

Это гарантирует вывод сообщения «Finally block executed» как при возникновении ошибки, так и без неё, демонстрируя, как finally можно использовать для выполнения необходимых завершающих действий.

Примеры реальных API-запросов

JSONPlaceholder API — отличный инструмент для практики работы с реальными данными в JavaScript, особенно при работе с асинхронными запросами и обработкой возможных ошибок. Ниже приведены несколько практических примеров с использованием JSONPlaceholder API, который предоставляет фиктивные данные REST для тестирования и прототипирования.

Пример 1: Получение записей и обработка ошибок

В этом примере мы получаем записи из JSONPlaceholder API с помощью fetch и обрабатываем возможные сетевые ошибки или проблемы с ответом API:


javascript— editable

Этот скрипт выполняет HTTP-запрос для получения одного элемента todo. Он проверяет успешность ответа (то есть статус HTTP 200-299). Если ответ неуспешный, выбрасывается ошибка со статусом ответа. Любые ошибки — как от сетевых проблем, так и от инструкции throw — перехватываются в блоке catch и записываются в журнал. Блок finally выполняется независимо от результата, обеспечивая завершение всех необходимых операций.

Пример 2: Отправка данных и обработка исключений

Здесь показано, как отправлять данные на сервер методом POST и корректно обрабатывать исключения:


javascript— editable

В этом скрипте мы отправляем новую запись на сервер. Функция fetch используется с методом POST, включая заголовки и тело в формате JSON. Если ответ сервера указывает на сбой (статус HTTP не из диапазона 2xx), выбрасывается ошибка, которая перехватывается и обрабатывается в блоке catch. Независимо от успеха или неудачи, блок finally гарантирует фиксацию завершения операции.

Пример 3: Намеренный вызов ошибки и её обработка

В этом примере намеренно запрашивается несуществующий на JSONPlaceholder API идентификатор пользователя, что вызывает ошибку 404 Not Found, которую мы перехватим и обработаем.


javascript— editable

Как работает этот пример

  1. Недопустимый API-эндпоинт: функция fetch вызывается с URL, содержащим недопустимый идентификатор пользователя (99999). Поскольку JSONPlaceholder обычно не имеет пользователя с таким индексом, API вернёт ошибку 404.
  2. Проверка корректности ответа: код проверяет, что статус ответа не входит в успешный диапазон (200-299). Поскольку идентификатор пользователя недопустим, ответ API скорее всего будет 404, что активирует обработку ошибок в проверке if (!response.ok).
  3. Выброс ошибки: так как ответ неуспешный, выбрасывается ошибка с сообщением, включающим статус HTTP, который в данном случае указывает на ошибку 404 Not Found.
  4. Блок Catch: блок catch перехватывает выброшенную ошибку и записывает конкретное сообщение с помощью console.log. Это даёт чёткую обратную связь о том, что пошло не так.
  5. Блок Finally: этот блок используется для очистки или финальных операций и сигнализирует о завершении попытки независимо от её результата.

Почему Try...Catch не может поймать ошибки асинхронных колбэков

Это самая распространённая ловушка при работе с try...catch. Данная инструкция является синхронной: она перехватывает только ошибки, выброшенные во время текущего выполнения блока try. Когда вы планируете колбэк через setTimeout, обработчик событий или ожидаемый промис, колбэк выполняется позже — после того, как try...catch уже завершился и стек вызовов пуст. К тому моменту нет никакого окружающего try, который мог бы что-то перехватить.

javascript— editable

Чтобы обработать ошибку, try...catch должен находиться внутри колбэка, там, где ошибка фактически выбрасывается:

javascript— editable

Это же правило объясняет, почему try...catch работает с async/await, но не с голыми промисами. await приостанавливает функцию и возобновляет её в том же логическом потоке, поэтому отклонённый промис проявляется как выброшенная ошибка, которую может поймать try. Промис, который не является await-ожидаемым, выполняется независимо и обходит окружающий try — вместо этого нужно использовать .catch().

javascript— editable

Для более детального изучения асинхронной обработки ошибок см. Обработка ошибок с промисами и async/await.

Заключение

Эффективная обработка ошибок в JavaScript является ключевым аспектом разработки высококачественных и устойчивых приложений. Использование try...catch позволяет корректно обрабатывать синхронные ошибки и отклонения await-промисов, сохраняя контроль над потоком выполнения приложения. Выбрасывайте объекты Error (никогда не голые строки), делайте ветвление по встроенным типам, таким как TypeError и SyntaxError, повторно выбрасывайте то, что не умеете обрабатывать, и помните об асинхронном ограничении: ошибки в колбэках и неожидаемых промисах требуют собственной обработки.

Связанные темы

Практика

Практика
Каково назначение инструкции try/catch в JavaScript?
Каково назначение инструкции try/catch в JavaScript?
Was this page helpful?