> Как обрабатывать ошибки в промисах в JavaScript? (JavaScript)

Уровень: senior · Роль: frontend · Категория: Технические вопросы

Компании: EPAM, Яндекс

Стек: JavaScript

> Пример ответа

Короткий ответ

Основные способы обработки ошибок в промисах: метод .catch() для цепочек промисов и блок try/catch с async/await. В .catch() можно обрабатывать ошибки на любом этапе цепочки, а try/catch даёт синхронный стиль. Важно всегда завершать цепочку промисов обработчиком ошибок, иначе reject останется необработанным.

Подробное объяснение

Ошибки в промисах обрабатываются через механизм reject - аналог throw для синхронного кода. Когда промис переходит в состояние rejected, управление передаётся ближайшему .catch() в цепочке. Если .catch() нет, возникает unhandled promise rejection, что в современных средах приводит к падению процесса (Node.js) или ошибке в консоли (браузер).

В async/await ошибки обрабатываются через try/catch, что делает код более читаемым. Однако важно помнить, что await сам по себе не ловит reject - нужно оборачивать вызовы. Также можно использовать .catch() внутри async функции, если нужно обработать ошибку конкретного промиса, не прерывая выполнение.

Сложные сценарии включают:

  • Обработка ошибок в параллельных промисах (Promise.all, Promise.allSettled, Promise.race)
  • Проброс ошибок через .then(null, handler) вместо .catch()
  • Восстановление после ошибки (fallback) через возврат значения в .catch()

На практике

В реальных проектах важно:

  • Всегда завершать цепочку .catch() или использовать try/catch в async функциях
  • Для параллельных операций использовать Promise.allSettled, если нужно получить результаты всех промисов, даже при ошибках
  • Логировать ошибки в .catch() и пробрасывать их дальше, если нужно централизованное управление
  • Избегать пустых .catch() - они скрывают ошибки
  • Использовать finally для очистки ресурсов (например, закрытие соединений)

Пример кода

JAVASCRIPT
// Базовый catch
fetch('/api/data')
.then(response => response.json())
.catch(error => console.error('Ошибка запроса:', error));
// async/await с try/catch
async function loadData() {
try {
const response = await fetch('/api/data');
return await response.json();
} catch (error) {
console.error('Ошибка:', error);
throw error; // проброс для внешней обработки
}
}
// Восстановление после ошибки
fetch('/api/primary')
.catch(() => fetch('/api/fallback'))
.then(data => process(data));
// Promise.allSettled для параллельных запросов
const results = await Promise.allSettled([fetch('/a'), fetch('/b')]);
const successful = results.filter(r => r.status === 'fulfilled').map(r => r.value);

Как отвечать на собеседовании

Начни с базовых механизмов (.catch и try/catch), затем перейди к нюансам: разница между .catch() и вторым аргументом .then(), поведение Promise.all vs Promise.allSettled, unhandled rejection. Покажи понимание, когда какой подход уместен. Упомяни, что в Node.js unhandled rejection может убить процесс, а в браузере - просто логируется. Если спросят про цепочки, объясни, как ошибка "прыгает" через .then() до ближайшего .catch().

Что проверяет интервьюер

  • Понимание асинхронного потока и event loop
  • Умение выбирать правильный паттерн обработки ошибок под задачу
  • Знание тонкостей: разница между .catch() и .then(null, handler), поведение async/await с rejected промисами
  • Опыт работы с параллельными промисами и обработкой частичных ошибок
  • Понимание последствий unhandled rejection в разных средах

Типичные ошибки

  • Забыть .catch() в конце цепочки - unhandled rejection
  • Использовать Promise.all без обработки ошибок, когда нужен allSettled
  • Путать .catch() с .then(null, handler) - второй вариант не ловит ошибки из самого обработчика
  • Не пробрасывать ошибку в catch, когда нужно централизованное логирование
  • Использовать try/catch вокруг await без понимания, что ошибка может быть поймана только если промис rejected

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью