Проанализируйте следующий JavaScript-кейс: const results = await Promise.all(tasks.map(async t => await fetch(t))); — как обрабатывать ошибки отдельных промисов, чтобы один провал не прервал все операции, и какие альтернативы Promise.all есть для частичной устойчивости?

11 Янв в 06:34
19 +1
0
Ответы
1
Коротко: `Promise.all` завершится с ошибкой при первом отклонённом (rejected) промисе. Чтобы один провал не прерывал все операции, нужно превратить каждый промис в «безопасный» — тот, который всегда резолвится с метаданными об успехе/ошибке — или использовать `Promise.allSettled`. Ниже практичные варианты и пояснения.
1) Обернуть каждый промис и вернуть объект статуса
- паттерн: каждый промис `.then(...).catch(...)` превращается в `{ status: 'fulfilled', value }` или `{ status: 'rejected', reason }`.
Пример:
```js
const wrapped = tasks.map(t => fetch(t)
.then(res => {
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res;
})
.then(value => ({ status: 'fulfilled', value }))
.catch(reason => ({ status: 'rejected', reason }))
);
const results = await Promise.all(wrapped);
// потом фильтруете:
const success = results.filter(r => r.status === 'fulfilled').map(r => r.value);
const failed = results.filter(r => r.status === 'rejected').map(r => r.reason);
```
Преимущество: полный контроль, можно вернуть запасные значения, логировать ошибки, считать число падений и т.д.
2) Использовать Promise.allSettled (чуть короче)
```js
const settled = await Promise.allSettled(tasks.map(t => fetch(t)));
// каждая запись: { status: 'fulfilled', value } или { status: 'rejected', reason }
```
Учтите: `fetch` не считает HTTP-ошибки (4xx/5xx) как rejection — нужно проверять `res.ok` внутри `then`.
3) Обработка в асинхронных функциях (try/catch внутри map)
```js
const promises = tasks.map(async t => {
try {
const res = await fetch(t);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return { status: 'fulfilled', value: res };
} catch (reason) {
return { status: 'rejected', reason };
}
});
const results = await Promise.all(promises);
```
Это то же самое, но более читабельно при сложной логике.
4) Альтернативы для других требований отказоустойчивости
- `Promise.any` — возвращает первый успешный результат; отклоняется только если все промисы отклоняются.
- `Promise.race` — вернёт первый settled (fulfill или reject); не даёт частичной устойчивости по умолчанию.
- Конкурентный лимит (p-limit, p-map, Bluebird.map) — уменьшает нагрузку/таймауты; можно оборачивать каждый вызов в таймаут/abort.
- Bluebird: `.reflect()` и `.settle()` дают похожую семантику на `allSettled`.
5) Полезные замечания
- Проверяйте `response.ok` у `fetch`, иначе 4xx/5xx не попадут в catch.
- При необходимости агрегировать ошибки — используйте `AggregateError` или соберите массив причин и бросьте вручную.
- Если нужно считать допустимое число ошибок, используйте `Promise.allSettled`, посчитайте `rejected` и бросьте, если превысило порог.
Резюме: для простого и надежного поведения используйте `Promise.allSettled` или оборачивайте каждый промис через `.catch`/try–catch, чтобы гарантировать, что один провал не прерывает все операции.
11 Янв в 06:43
Не можешь разобраться в этой теме?
Обратись за помощью к экспертам
Гарантированные бесплатные доработки в течение 1 года
Быстрое выполнение от 2 часов
Проверка работы на плагиат
Поможем написать учебную работу
Прямой эфир