Ожидание завершения всех Promises, независимо от ошибок
Быстрый ответ
Если появилась необходимость дождаться завершения всех промисов в JavaScript, независимо от их результата — положительного или отрицательного, стоит применить метод Promise.allSettled()
. Он группирует массив промисов и возвращает массив результатов, отражающих состояние каждого отдельного промиса.
// Пример с массивом промисов, выполняющих запросы к 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
.
Более подробно о Promise.allSettled()
Детально рассмотрим использование Promise.allSettled
для обработки результатов и как себя вести, если данный метод отсутствует.
Обработка результатов allSettled
После получения результатов от Promise.allSettled
, нередко возникает желание разделить успешно выполненные промисы от отклонённых. Это можно осуществить, отфильтровав результаты по их status
.
// Пример разделения промисов на успешно выполненные и отклонённые
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
, который будет воспроизводить его функциональность.
// Полифил для `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
. Это гарантирует обработку всех промисов, делая их выполнение или отклонение предсказуемым.
// Функция `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
) позволяет нормализовать обработку ошибок и более надежно работать с результатами.
// Приведение промисов к единому формату.
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 – "делай проще". В большинстве случаев достаточно стандартного сценария обработки ошибок без лишних сдвигов и усложнений.
// Обработка результатов со стандартной обработкой ошибок.
Promise.allSettled(promises).then(results => {
results.forEach(result => {
const data = result.status === 'fulfilled' ? result.value : undefined; // Работаем с данными, ошибки обрабатываем как undefined.
});
});
Грациозное управление сетевыми проблемами
При сетевых запросах следует эффективно управлять возможными сбоями, включая медленное соединение, тайм-ауты или серверные сбои – это повседневная реальность каждого разработчика.
// Обертка для 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);
Визуализация
Воспринимайте ситуацию с промисами как гонку, где каждый промис – это бегун. Один из них может дойти до финиша, второй может столкнуться с трудностями и потребовать помощи.
🏃♂️💨 ➡️ 🏁
🏃♂️🤕 ➡️ 🚑
Гонка заканчивается тогда, когда все бегуны добежали до финиша или получили необходимую помощь.
🚩 **Старт**
🏃♂️💨 ➡️ 🏁 ✓
🏃♂️🤕 ➡️ 🚑 ✓
🚩 **Все участники учтены**
Наши задача и миссия – дождаться всех, независимо от их результатов.
Promise.allSettled([🏃♂️💨, 🏃♂️🤕]) ➡️ 🏁🚑
Собираем результаты и продолжаем нашу работу после того, как все исходы будут учтены.
Полезные материалы
- Promise.allSettled() – JavaScript | MDN – подробнее о методе
Promise.allSettled
и его использовании. - Использование промисов – JavaScript | MDN – руководство по работе с промисами и их цепочками.
- Промисы, async/await – глубокое погружение в промисы в JavaScript и связанные с ними асинхронные паттерны.
- Util | Документация Node.js v21.6.1 – обзор утилиты в Node.js для преобразования колбэков в промисы.
- Promise.all() – JavaScript | MDN – пояснения о работе
Promise.all
и обработке ошибок в данном контексте.