> Как определить момент завершения всех промисов с ошибками для вызова reject в JavaScript? (JavaScript)

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

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

Стек: JavaScript

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

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

Для определения момента завершения всех промисов с ошибками используйте Promise.allSettled(). Он ждёт выполнения всех промисов независимо от результата и возвращает массив объектов с полями status, value или reason. Если нужен reject при первой же ошибке - Promise.all(). Для кастомной логики сбора ошибок подойдёт Promise.all() в комбинации с map и обработкой каждого промиса через .catch.

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

В JavaScript есть несколько способов работы с группой промисов, и выбор зависит от требуемого поведения при ошибках:

  • Promise.all() - завершается reject при первой ошибке, не дожидаясь остальных промисов. Это подходит, когда любая ошибка критична и дальнейшее выполнение не имеет смысла.

  • Promise.allSettled() - ждёт завершения всех промисов, возвращая массив результатов. Каждый элемент содержит status: "fulfilled" или "rejected", а также value или reason. Идеален, когда нужно обработать все результаты, включая ошибки.

  • Promise.any() - завершается resolve при первом успешном промисе, reject - только если все промисы отклонены. Полезен для race-условий с fallback.

  • Promise.race() - завершается при первом завершённом промисе (любого статуса). Используется для таймаутов.

Для кастомного сбора ошибок можно обернуть каждый промис в конструкцию, которая никогда не reject'ится, а накапливает ошибки в массиве:

JAVASCRIPT
function reflect(promise) {
return promise.then(
value => ({ status: 'fulfilled', value }),
reason => ({ status: 'rejected', reason })
);
}

Этот паттерн лежит в основе Promise.allSettled().

На практике

В реальных проектах выбор метода зависит от сценария:

  • Загрузка данных для UI: используйте Promise.allSettled(), чтобы показать частично загруженную страницу с индикацией ошибок по отдельным блокам.

  • Валидация формы с несколькими async-проверками: Promise.all() с reject при первой ошибке, чтобы сразу показать пользователю проблему.

  • Параллельные запросы к API с fallback-серверами: Promise.any() для автоматического переключения.

  • Таймаут для запроса: Promise.race() с промисом, который reject'ится через заданное время.

Важно помнить, что Promise.allSettled() появился в ES2020. Для старых окружений требуется полифилл или ручная реализация через reflect.

Пример кода

JAVASCRIPT
// Сценарий: загрузка данных для дашборда
const fetchUser = fetch('/api/user').then(r => r.json());
const fetchOrders = fetch('/api/orders').then(r => r.json());
const fetchStats = fetch('/api/stats').then(r => r.json());
// Вариант 1: все или ничего
Promise.all([fetchUser, fetchOrders, fetchStats])
.then(([user, orders, stats]) => renderDashboard(user, orders, stats))
.catch(err => showErrorPage(err));
// Вариант 2: частичная загрузка с ошибками
Promise.allSettled([fetchUser, fetchOrders, fetchStats])
.then(results => {
const data = results.map(r =>
r.status === 'fulfilled' ? r.value : null
);
renderDashboardWithFallbacks(data);
});
// Вариант 3: кастомный сбор ошибок
const promises = [fetchUser, fetchOrders, fetchStats];
const errors = [];
Promise.all(
promises.map(p =>
p.catch(err => { errors.push(err); return null; })
)
).then(results => {
if (errors.length) notifyErrors(errors);
renderDashboard(results);
});

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

Начните с прямого ответа: Promise.allSettled() для ожидания всех промисов с ошибками. Затем объясните trade-off между разными методами. Покажите понимание, когда какой метод применять. Упомяните, что Promise.allSettled() - это сахар над паттерном reflect. Если спросят про старые браузеры, предложите полифилл или ручную реализацию. Не углубляйтесь в микрооптимизации - сфокусируйтесь на семантике и практической применимости.

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

  • Понимание асинхронности и жизненного цикла промисов
  • Знание стандартных методов Promise API и их различий
  • Умение выбирать правильный инструмент под задачу
  • Понимание edge cases: утечки памяти, порядок выполнения, обработка ошибок
  • Способность объяснить trade-off между подходами

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

  • Использование Promise.all() когда нужно дождаться всех результатов, включая ошибки
  • Забывание, что Promise.all() reject'ится немедленно при первой ошибке
  • Путаница между Promise.allSettled() и Promise.any()
  • Отсутствие обработки ошибок в каждом промисе при кастомном сборе
  • Предположение, что промисы выполняются последовательно при использовании Promise.all() - на самом деле они запускаются параллельно

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

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