Обработка ошибок в асинхронном коде: try/catch и Promise

Надежное приложение должно корректно обрабатывать ошибки. В асинхронном JavaScript это сложнее, чем в синхронном, из-за разрыва стека вызовов. Неправильная обработка может привести к «тихим» сбоям, которые трудно отладить.

Обработка в Promise и async/await

С появлением async/await код стал выглядеть синхронным, но ошибки все равно нужно ловить через try/catch. Важно помнить, что await превращает отклоненный Promise в исключение.

async function fetchData() {
  try {
    const response = await fetch('/api/data');
    if (!response.ok) throw new Error('Network error');
    const data = await response.json();
    return data;
  } catch (error) {
    console.error('Ошибка загрузки:', error);
    // Логирование или показ UI ошибки
    return null;
  }
}

Если вы забудете try/catch вокруг await, ошибка уйдет в глобальный обработчик unhandledrejection, и выполнение функции прервется.

Цепочки Promise и catch

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

fetch('/api/user')
  .then(res => res.json())
  .catch(err => {
    console.log('Поймали ошибку, возвращаем дефолт');
    return { name: 'Guest' }; // Цепочка продолжается
  })
  .then(data => {
    console.log('Данные:', data); // Выполнится даже при ошибке выше
  });

Глобальная обработка ошибок

Для продакшена обязательно настройте глобальные перехватчики. В браузере это window.onerror и window.onunhandledrejection.

window.addEventListener('unhandledrejection', event => {
  event.preventDefault(); // Чтобы не шумело в консоли
  console.error('Необработанная Promise ошибка:', event.reason);
  // Отправка отчета на сервер мониторинга
});

Всегда завершайте цепочки промисов блоком .catch() или оборачивайте await в try/catch. «Проглоченные» ошибки — главная причина нестабильности JS-приложений.