W3docs

Обработка ошибок в Promise в JavaScript

Обработка ошибок в промисах JavaScript: .catch(), распространение отказов, перебрасывание, .finally(), событие unhandledrejection и try/catch с async/await.

Промисы JavaScript — это фундаментальный механизм управления асинхронными операциями, позволяющий разработчикам обрабатывать асинхронные события с большей гибкостью (см. JavaScript: Promises). Обработка ошибок в промисах критически важна для написания надёжного JavaScript-кода, способного справляться с непредвиденными ситуациями без краша приложения.

В этой главе рассматривается, как перехватываются отказы, как ошибки передаются по цепочке, как выполняется перебрасывание и восстановление, хук очистки .finally(), глобальная защитная сеть unhandledrejection, а также то, как эти же паттерны применяются в async/await. Если вы только начинаете знакомиться с цепочками .then(), сначала прочитайте Цепочки промисов.

Промис может завершиться в одном из двух состояний: fulfilled (выполнен с результатом) или rejected (произошла ошибка). Отказ возникает, когда вызывается reject(...), когда внутри исполнителя или колбэка .then() выбрасывается исключение с помощью throw, или когда встроенный асинхронный API завершается ошибкой. Обработка ошибок — это перенаправление таких отказов к обработчику вместо того, чтобы дать им аварийно завершить программу.

Обработка ошибок в промисах реализуется с помощью метода .catch() или путём передачи второго аргумента методу .then(). Оба способа позволяют управлять ошибками, возникающими в ходе выполнения асинхронных операций, и восстанавливаться после них.

Использование метода .catch()

Метод .catch() используется для перехвата любых ошибок, возникающих в цепочке промисов.


javascript— editable

Использование второго аргумента .then()

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


javascript— editable

Продвинутые техники обработки ошибок

Поток ошибок в цепочках

Ошибки должны корректно распространяться по цепочке промисов, чтобы они обрабатывались на нужном уровне. Например, если блок .catch() расположен перед блоком .then(), блок .then() всё равно выполнится. Поскольку .catch() переводит цепочку промисов в состояние fulfilled (если только не перебрасывает ошибку), следующий .then() получает undefined в качестве аргумента.

Внимание

Если блок .catch() расположен перед блоком .then(), любая ошибка, выброшенная внутри .then(), не будет перехвачена предшествующим .catch(). Она будет обработана только при наличии ещё одного блока .catch() после него.


javascript— editable

Распространение отказов

После каждого .then() не обязательно добавлять .catch(). Отказ пропускает все обработчики успеха (первый аргумент .then()) и продвигается по цепочке, пока не достигнет первого .catch(). Это позволяет написать длинную цепочку шагов и обработать любую ошибку в одном месте в конце.

В приведённом ниже примере отказ происходит в самом начале, однако ни один из трёх колбэков .then() не выполняется — управление переходит сразу к единственному .catch():


javascript— editable

Перебрасывание и восстановление

Обработчик .catch() выполняет две функции в зависимости от своего поведения:

  • Восстановление — если он возвращает значение (или ничего не возвращает), цепочка снова становится fulfilled и следующий .then() получает это значение. Так реализуется подстановка запасного значения.
  • Перебрасывание — если он выбрасывает исключение с помощью throw (или возвращает отклонённый промис), ошибка продолжает распространяться к следующему .catch(). Используйте это, когда обработчик не может полностью справиться с ошибкой и хочет передать её следующему обработчику.

javascript— editable

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

JavaScript допускает более гибкие стратегии обработки ошибок, например фильтрацию ошибок по их типу или конкретным обстоятельствам. В следующем примере обрабатывается TypeError. TypeError обычно возникает, когда значение не соответствует ожидаемому типу, и выполнить желаемую операцию невозможно.


javascript— editable

Лучшие практики обработки ошибок в промисах

  1. Всегда возвращайте или перебрасывайте ошибки в блоках catch, чтобы ошибки не игнорировались молча.
  2. Правильно выстраивайте цепочки промисов, чтобы ошибки перехватывались и обрабатывались.
  3. Используйте блоки finally там, где необходимо выполнить задачи очистки, независимо от результата промиса.

Реализация блока finally

Метод finally() используется для выполнения блока кода после завершения промиса, независимо от результата.


javascript— editable

Перехват необработанных отказов

Если промис отклоняется и ни один .catch() его не обрабатывает, ошибка молча теряется — в отличие от синхронного кода, здесь нет окружающего try/catch. Чтобы ошибки не исчезали незамеченными, среда выполнения генерирует глобальное событие unhandledrejection, которое можно прослушать. Это последнее средство защиты для логирования и отчётности, а не замена настоящему .catch().

В браузере:


javascript— editable

В Node.js аналогом является process.on('unhandledrejection', (reason) => { ... }).

Обработка ошибок с async/await

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


javascript— editable
Информация

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

Заключение

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

Для углублённого изучения продолжайте читать Цепочки промисов и Promise API.

Практика

Практика
Что является ключевой концепцией при обработке ошибок в промисах JavaScript?
Что является ключевой концепцией при обработке ошибок в промисах JavaScript?
Was this page helpful?