Ожидание завершения всех Promises, независимо от ошибок

Пройдите тест, узнайте какой профессии подходите

Я предпочитаю
0%
Работать самостоятельно и не зависеть от других
Работать в команде и рассчитывать на помощь коллег
Организовывать и контролировать процесс работы

Быстрый ответ

Если появилась необходимость дождаться завершения всех промисов в JavaScript, независимо от их результата — положительного или отрицательного, стоит применить метод Promise.allSettled(). Он группирует массив промисов и возвращает массив результатов, отражающих состояние каждого отдельного промиса.

JS
Скопировать код
// Пример с массивом промисов, выполняющих запросы к API
const promises = [fetch('/api/one'), fetch('/api/two')];

Promise.allSettled(promises).then((results) =>
  results.forEach(({status, value, reason}) =>
    console.log(status, value || reason))); // Выводим информацию в консоль

Результат каждого промиса будет содержать status ('fulfilled' или 'rejected'), при успешном исходе – value, при ошибке – reason.

Кинга Идем в IT: пошаговый план для смены профессии

Более подробно о Promise.allSettled()

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

Обработка результатов allSettled

После получения результатов от Promise.allSettled, нередко возникает желание разделить успешно выполненные промисы от отклонённых. Это можно осуществить, отфильтровав результаты по их status.

JS
Скопировать код
// Пример разделения промисов на успешно выполненные и отклонённые
Promise.allSettled(promises).then((results) => {
  const successfulPromises = results.filter(result => result.status === 'fulfilled');
  const failedPromises = results.filter(result => result.status === 'rejected');
  // Теперь у вас есть возможность отдельно обработать успешные промисы и те, что были отклонены.
});

Работа с промисами в ES5

Если в вашей рабочей среде нет поддержки ES2020 и более новых версий, метод Promise.allSettled может быть недоступен. В таком случае можно использовать полифил или создать свой метод settle, который будет воспроизводить его функциональность.

JS
Скопировать код
// Полифил для `Promise.allSettled`, позволяющий использовать его в старых версиях сред.
if (!Promise.allSettled) {
  Promise.allSettled = promises =>
    Promise.all(
      promises.map(promise =>
        promise.then(value => ({ status: 'fulfilled', value }))
               .catch(reason => ({ status: 'rejected', reason }))));
}

Полифилл позволит вам вызывать Promise.allSettled даже в устаревших версиях среды.

Улучшение обработки ошибок

Кроме использования Promise.allSettled, есть подход, предполагающий индивидуальный перехват ошибок для каждого промиса с помощью функции reflect. Это гарантирует обработку всех промисов, делая их выполнение или отклонение предсказуемым.

JS
Скопировать код
// Функция `reflect` для перехвата и обработки ошибок каждого промиса.
function reflect(promise) {
  return promise.then(
    value => ({ status: 'fulfilled', value }),
    reason => ({ status: 'rejected', reason })
  );
}

// Обработка всех промисов с использованием функции `reflect`.
Promise.all(promises.map(reflect)).then(results => {
  // Контроль над всеми ошибками установлен.
});

Необходимость обработки результатов

Ожидание завершения выполнения всех промисов – это лишь часть задачи. Также важно эффективно управлять результатами каждого из них. Давайте узнаем, как можно использовать детальную информацию, которую предоставляет Promise.allSettled.

Обеспечение стандартизации интерфейса промисов

Обеспечение стандартизованного интерфейса результатов промисов (с status и, в зависимости от исхода, value или reason) позволяет нормализовать обработку ошибок и более надежно работать с результатами.

JS
Скопировать код
// Приведение промисов к единому формату.
function adjustPromises(promises) {
  return promises.map(promise => reflect(promise));
}

// Работа со стандартизированными промисами.
const adjustedPromises = adjustPromises([fetch(url1), fetch(url2)]);
Promise.allSettled(adjustedPromises).then(results => { /* Работаем с результатами */ });

Отказ от использования внешних библиотек

Современные средства JavaScript для работы с промисами делают лишними сторонние библиотеки. Наличие Promise.allSettled нативно сводит к минимуму необходимость добавления дополнительных зависимостей.

Упрощение обработки ошибок

Иногда меньше обозначает больше, особенно когда речь идет об обработке ошибок. Здесь действует принцип KISS – "делай проще". В большинстве случаев достаточно стандартного сценария обработки ошибок без лишних сдвигов и усложнений.

JS
Скопировать код
// Обработка результатов со стандартной обработкой ошибок.
Promise.allSettled(promises).then(results => {
  results.forEach(result => {
    const data = result.status === 'fulfilled' ? result.value : undefined; // Работаем с данными, ошибки обрабатываем как undefined.
  });
});

Грациозное управление сетевыми проблемами

При сетевых запросах следует эффективно управлять возможными сбоями, включая медленное соединение, тайм-ауты или серверные сбои – это повседневная реальность каждого разработчика.

JS
Скопировать код
// Обертка для fetch с установкой таймаута, чтобы не ждать медленные API
function fetchWithTimeout(url, timeout = 1000) {
  return new Promise((resolve, reject) => {
    setTimeout(() => reject(new Error('timeout')), timeout); // Терпение – долг, но не в случае с тайм-аутами
    fetch(url).then(resolve, reject);
  });
}

// Встречаемся с промисами вместе, готовы?
Promise.allSettled(promises.map(fetchWithTimeout)).then(handleAllPromises);

Визуализация

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

Markdown
Скопировать код
🏃‍♂️💨 ➡️ 🏁
Markdown
Скопировать код
🏃‍♂️🤕 ➡️ 🚑

Гонка заканчивается тогда, когда все бегуны добежали до финиша или получили необходимую помощь.

Markdown
Скопировать код
🚩 **Старт**
🏃‍♂️💨 ➡️ 🏁 ✓
🏃‍♂️🤕 ➡️ 🚑 ✓
🚩 **Все участники учтены**

Наши задача и миссия – дождаться всех, независимо от их результатов.

Markdown
Скопировать код
Promise.allSettled([🏃‍♂️💨, 🏃‍♂️🤕]) ➡️ 🏁🚑

Собираем результаты и продолжаем нашу работу после того, как все исходы будут учтены.

Полезные материалы

  1. Promise.allSettled() – JavaScript | MDN – подробнее о методе Promise.allSettled и его использовании.
  2. Использование промисов – JavaScript | MDN – руководство по работе с промисами и их цепочками.
  3. Промисы, async/await – глубокое погружение в промисы в JavaScript и связанные с ними асинхронные паттерны.
  4. Util | Документация Node.js v21.6.1 – обзор утилиты в Node.js для преобразования колбэков в промисы.
  5. Promise.all() – JavaScript | MDN – пояснения о работе Promise.all и обработке ошибок в данном контексте.