Журнал • знания • практикаПоиск по сайту

Сайты, дизайн и самостоятельная работа

Найти материал →

Обработка ошибок в асинхронном javascript без дублирования логики

6 минут чтения

Обработка ошибок в асинхронном javascript без дублирования логики

Чтобы обрабатывать асинхронные ошибки в JavaScript без дублирования, сначала определите, где их перехватывать: рядом с конкретной операцией или на общей границе приложения. Повторяющиеся действия - например, журналирование и добавление контекста - вынесите в утилиту, а восстановление после сбоя оставьте коду, который знает, как продолжить работу.

Опорные правила обработки асинхронных сбоев

  • Перехватывайте исключение там, где можно принять решение: повторить операцию, показать сообщение или прекратить сценарий.
  • Используйте общий слой для повторяемых действий, но не превращайте его в универсальное место для подавления ошибок.
  • При повторном выбросе сохраняйте исходную причину через свойство cause или исходный объект ошибки.
  • Считайте отмену запроса отдельным исходом, а не обычным сбоем.
  • Проверяйте обработку ошибок в JavaScript для одиночных запросов, цепочек промисов и параллельных задач.

Проверьте, где именно возникают ошибки: промисы, async-функции и внешние API

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

Такой разбор полезен при разработке веб-приложений на JavaScript, когда сбои повторяются в нескольких сценариях. Не добавляйте общий обработчик только ради архитектурной симметрии: он не нужен, если операции уже имеют разные правила восстановления и повторяющийся код отсутствует.

Сначала определите границы ответственности за перехват ошибок

Перед изменением кода решите, какой уровень отвечает за каждое действие:

  • Функция запроса может добавить контекст, связанный с API или ресурсом.
  • Сценарий приложения решает, что делать дальше: показать состояние ошибки, предложить повтор или прекратить действие.
  • Общий обработчик выполняет повторяющиеся технические задачи, например форматирует событие для журнала.

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

Соберите повторяющуюся обработку в общий утилитарный слой

Перед изменениями подготовьте короткий список проверок:

  • Найдите одинаковые блоки catch и определите, что в них действительно повторяется.
  • Отделите журналирование и добавление контекста от решений интерфейса.
  • Уточните, как проект представляет ошибки и отменённые запросы.
  • Выберите безопасный тестовый сценарий, не затрагивающий реальные пользовательские данные.
  1. Опишите повторяемое поведение.
    Зафиксируйте, что утилита должна делать при сбое, например добавлять контекст и передавать ошибку дальше. Не включайте в неё показ уведомлений, если разные экраны должны реагировать по-разному.
  2. Создайте функцию для дополнения контекста.
    Она может обернуть исходную ошибку, сохранив её как причину:

    function withContext(error, message) {
      return new Error(message, { cause: error });
    }

    Поддержка cause зависит от среды выполнения. Если целевые среды проекта её не поддерживают, используйте согласованный проектом способ сохранить исходную ошибку.

  3. Примените утилиту на границе операции.
    Обработайте ошибку там, где известен полезный контекст, и передайте её следующему уровню:

    async function loadProfile(id) {
      try {
        return await fetchProfile(id);
      } catch (error) {
        throw withContext(error, `Не удалось загрузить профиль ${id}`);
      }
    }
  4. Оставьте решение вызывающему коду.
    Вызывающая функция может показать сообщение или выполнить восстановление. Не перехватывайте ошибку без дальнейшего действия: иначе сбой будет выглядеть как успешное выполнение.
  5. Проверьте повторное использование.
    Подключайте утилиту к другим функциям только там, где совпадает поведение. Если различаются правила восстановления, оставьте их локальными.

Такой подход подходит и для разработки сайта на JavaScript: общий слой устраняет дублирование технических действий, но не скрывает смысл ошибки от кода, который управляет страницей.

Разделяйте восстановимые сбои, отмену запроса и критические исключения

Проверьте результат внедрения по списку:

  • При временном сбое предусмотрено ли безопасное повторение или понятный отказ?
  • Показывает ли интерфейс сообщение, соответствующее действию пользователя?
  • Не выдаётся ли отмена запроса за ошибку, требующую уведомления?
  • Передаётся ли критическое исключение выше, если текущий уровень не может его обработать?
  • Сохраняется ли исходная причина после добавления контекста?
  • Не выполняется ли одна и та же обработка одновременно в утилите и вызывающей функции?
  • Остаётся ли состояние приложения согласованным после неудачного запроса?

Для запросов с AbortController проверьте признак отмены отдельно. Не следует автоматически повторять отменённую пользователем операцию или показывать ему сообщение о сбое сервера.

Настройте единый формат логов, не теряя исходный контекст ошибки

При проверке кода обратите внимание на распространённые ошибки:

  • Перехват без повторного выброса или альтернативного результата, из-за которого отказ промиса пропадает.
  • Повторный выброс нового сообщения без сохранения исходной причины.
  • Журналирование только текста ошибки без сведений об операции и контексте.
  • Запись чувствительных данных, токенов или содержимого пользовательского запроса в журнал.
  • Обработка отмены запроса как критического сбоя.
  • Автоматический повтор операции без проверки, безопасно ли её выполнять повторно.
  • Дублирование одного события в нескольких слоях, из-за которого журналы становятся шумными.
  • Одинаковая реакция на все ошибки, хотя способы восстановления различаются.

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

Перед внедрением проверьте сценарии с цепочками промисов и параллельными задачами

Выберите подход по тому, кто должен принимать решение о реакции на сбой:

  • Локальный try...catch. Подходит, когда функция знает, как восстановиться, например вернуть запасное значение или обновить состояние интерфейса.
  • Общая утилита для контекста и логов. Уместна, когда несколько операций выполняют одинаковую техническую обработку, но решения о продолжении остаются разными.
  • Обработчик на границе приложения. Используйте его для фиксации ошибок, дошедших до верхнего уровня; он не заменяет обработку в местах, где требуется восстановление.
  • Обработка результата параллельных задач. Если нужно получить сведения об успехе и отказе каждой операции, рассмотрите Promise.allSettled; если достаточно остановиться при первом отказе, проверьте, подходит ли Promise.all.

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

Профиль автора материала

Виктор Егоров

Нюансы, которые влияют на надёжность обработки ошибок

Можно ли перехватить все асинхронные ошибки одним обработчиком?

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

Когда не стоит оборачивать ошибку новым объектом?

Не делайте этого, если новый объект теряет исходную причину или не добавляет полезного контекста. Если важно сохранить тип ошибки для последующих проверок, передавайте исходный объект без обёртки либо используйте согласованный способ сохранить тип и причину.

Нужно ли повторно выбрасывать ошибку после записи в журнал?

Если текущая функция не обработала сбой и не вернула допустимый альтернативный результат, ошибку нужно передать дальше. Иначе вызывающий код может ошибочно считать операцию успешной.

Как отличить отмену запроса от отказа API?

Проверяйте сигнал или тип ошибки, используемые вашей средой и HTTP-клиентом для отмены. Отмена часто означает прекращение ненужной операции и обычно не требует того же сообщения, что отказ сервера.

Что делать, если одна задача в Promise.all завершилась ошибкой?

Promise.all отклоняется при отказе одной из переданных задач. Если нужно обработать результат каждой задачи независимо, рассмотрите Promise.allSettled и явно разберите статусы результатов.

Какие данные безопасно включать в журнал?

Записывайте сведения, нужные для диагностики, например тип операции и контекст сбоя. Не включайте секреты и персональные данные без обоснованной необходимости и предусмотренных мер защиты.

Scroll to Top