> Как перехватывать ошибки в промисах и чем это отличается от try-catch (JavaScript)

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

Компании: Kaspersky

Стек: JavaScript

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

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

Перехват ошибок в промисах осуществляется через метод .catch() или второй колбэк в .then(). Основное отличие от try-catch в том, что .catch() работает асинхронно и перехватывает rejections на любом этапе цепочки промисов, тогда как try-catch синхронен и не ловит ошибки внутри асинхронных операций без await. Для асинхронного кода с async/await try-catch становится эквивалентом .catch().

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

Промисы предоставляют встроенный механизм обработки ошибок через .catch(), который автоматически перехватывает rejections и исключения, возникшие в предыдущих .then(). Это позволяет строить цепочки с единой точкой обработки ошибок. try-catch в синхронном коде работает только с синхронными исключениями - он не может перехватить rejection промиса, если не используется await. При использовании async/await try-catch становится синтаксическим сахаром над .catch(), но с важным нюансом: await может выбросить только rejected промис, а не синхронное исключение внутри промиса (оно превращается в rejection автоматически).

Ключевое различие - в propagation: .catch() в цепочке промисов может быть расположен после нескольких .then(), перехватывая ошибки из любого предыдущего звена. try-catch же работает только в рамках одного блока. Также .catch() возвращает новый промис, позволяя продолжить цепочку после обработки ошибки, что невозможно в try-catch без дополнительных конструкций.

На практике

В современном коде с async/await предпочтительнее использовать try-catch для читаемости, но .catch() остаётся актуальным для:

  • обработки ошибок в цепочках без await
  • случаев, когда нужно перехватить ошибку и продолжить цепочку (recover)
  • работы с Promise.all, Promise.race и другими комбинаторами

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

Пример кода

JAVASCRIPT
// Синхронный try-catch не ловит асинхронные ошибки
try {
Promise.reject('ошибка');
} catch (e) {
// Этот блок не выполнится
}
// Правильно: .catch()
Promise.reject('ошибка')
.catch(err => console.log(err)); // 'ошибка'
// async/await эквивалент
async function handleError() {
try {
await Promise.reject('ошибка');
} catch (err) {
console.log(err); // 'ошибка'
}
}
// Восстановление после ошибки через .catch()
fetch('/api/data')
.then(response => response.json())
.catch(() => ({ data: 'default' })) // возвращаем fallback
.then(data => console.log(data));

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

Начни с ключевого отличия - синхронность vs асинхронность. Подчеркни, что .catch() работает на уровне промисов, а try-catch - на уровне синхронного кода. Упомяни, что с async/await различие стирается, но .catch() даёт больше гибкости в цепочках. Приведи пример, где try-catch не сработает без await. Покажи понимание propagation и recovery. Избегай излишней теории - сфокусируйся на практических сценариях.

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

  • понимание асинхронной природы JavaScript
  • знание механизма rejection propagation в промисах
  • умение различать синхронные и асинхронные ошибки
  • способность выбрать правильный инструмент под задачу
  • знание тонкостей async/await и его связи с промисами

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

  • попытка использовать try-catch без await для ловли rejections
  • забывание, что .catch() возвращает новый промис, и попытка продолжить цепочку после него без return
  • путаница между синхронным throw и асинхронным rejection
  • игнорирование того, что ошибка в .catch() может быть перехвачена следующим .catch() в цепочке
  • предположение, что try-catch в async функции ловит все ошибки (он не ловит ошибки из других асинхронных операций без await)

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

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