На JavaScript/Node.js есть пример с промисами: Promise.all([p1,p2]).then(results=>{/*...*/}).catch(e=>{/*...*/}) — объясните поведение при частичном провале промисов, как обрабатывать частичные ошибки и какие абстракции (allSettled, race) полезны
Коротко — что происходит и что использовать. 1) Поведение `Promise.all` - `Promise.all([p1, p2, ...])` возвращает промис, который: - разрешится в массив значений, если все входные промисы выполнены (fulfilled) — `then(results => ...)`. - отклонится (reject) немедленно при первом отклонённом (rejected) промисе, с тем же `reason` — `catch(e => ...)`. - Важно: «немедленно» значит, что результат общего промиса — reject как только один промис отклонится; остальные промисы при этом продолжают выполняться в фоне. Их возможные последующие rejections будут считаться необработанными, если вы их явно не поймали. 2) Как обрабатывать частичные ошибки - Если нужна информация по каждому промису (успех/ошибка), используйте `Promise.allSettled`: ```js const results = await Promise.allSettled([p1, p2, p3]); // results = [{status: 'fulfilled', value: ...}, {status: 'rejected', reason: ...}, ...] const successes = results.filter(r => r.status === 'fulfilled').map(r => r.value); const errors = results.filter(r => r.status === 'rejected').map(r => r.reason); ``` - Альтернативный шаблон — обернуть каждый промис так, чтобы он никогда не отклонялся, а возвращал tagged-объект: ```js function reflect(p) { return p.then(v => ({ ok: true, value: v })) .catch(e => ({ ok: false, error: e })); } const results = await Promise.all([p1, p2, p3].map(reflect)); // затем фильтруете по .ok ``` Этот подход полезен, если хотите продолжать работу со всеми результатами и избежать `Promise.all`-reject. - Если нужно выполнить операции по очереди и при ошибке обрабатывать отдельно, можно `try/catch` вокруг каждого `await` или внутри цикла `for ... of`. 3) Полезные абстракции и паттерны - `Promise.allSettled(promises)` — получает статусы всех промисов (полезно для частичных успехов). - `Promise.race(promises)` — возвращает первый settled (fulfill или reject). Полезно для таймаутов: ```js const timeout = ms => new Promise((_, rej) => setTimeout(() => rej(new Error('timeout')), ms)); const result = await Promise.race([longRunningOp(), timeout(5000)]); ``` - `Promise.any(promises)` — разрешится при первом выполненном промисе; отклонится (с `AggregateError`), если все промисы отклонятся. - `Promise.all` — быстрый fail-fast; подходит, когда вам нужны все результаты или нет смысла продолжать при любой ошибке. 4) Практические рекомендации - Если ошибка одного запроса не должна ломать остальные — используйте `allSettled` или `reflect` и обрабатывайте per-item. - Чтобы избежать unhandled rejections, всегда поймайте возможные ошибки каждого промиса либо явно обработайте результат `allSettled` / обёртку. - Для таймаутов и «первого ответа» используйте `race`; для «первого успешного» — `any`. - Для сбора всех ошибок (например, логирования) используйте `allSettled` и собирайте `reason`. Если нужно — могу показать конкретный пример для вашей задачи (фоновая загрузка множества URLов, групповой rollback и т. п.).
1) Поведение `Promise.all`
- `Promise.all([p1, p2, ...])` возвращает промис, который:
- разрешится в массив значений, если все входные промисы выполнены (fulfilled) — `then(results => ...)`.
- отклонится (reject) немедленно при первом отклонённом (rejected) промисе, с тем же `reason` — `catch(e => ...)`.
- Важно: «немедленно» значит, что результат общего промиса — reject как только один промис отклонится; остальные промисы при этом продолжают выполняться в фоне. Их возможные последующие rejections будут считаться необработанными, если вы их явно не поймали.
2) Как обрабатывать частичные ошибки
- Если нужна информация по каждому промису (успех/ошибка), используйте `Promise.allSettled`:
```js
const results = await Promise.allSettled([p1, p2, p3]);
// results = [{status: 'fulfilled', value: ...}, {status: 'rejected', reason: ...}, ...]
const successes = results.filter(r => r.status === 'fulfilled').map(r => r.value);
const errors = results.filter(r => r.status === 'rejected').map(r => r.reason);
```
- Альтернативный шаблон — обернуть каждый промис так, чтобы он никогда не отклонялся, а возвращал tagged-объект:
```js
function reflect(p) {
return p.then(v => ({ ok: true, value: v }))
.catch(e => ({ ok: false, error: e }));
}
const results = await Promise.all([p1, p2, p3].map(reflect));
// затем фильтруете по .ok
```
Этот подход полезен, если хотите продолжать работу со всеми результатами и избежать `Promise.all`-reject.
- Если нужно выполнить операции по очереди и при ошибке обрабатывать отдельно, можно `try/catch` вокруг каждого `await` или внутри цикла `for ... of`.
3) Полезные абстракции и паттерны
- `Promise.allSettled(promises)` — получает статусы всех промисов (полезно для частичных успехов).
- `Promise.race(promises)` — возвращает первый settled (fulfill или reject). Полезно для таймаутов:
```js
const timeout = ms => new Promise((_, rej) => setTimeout(() => rej(new Error('timeout')), ms));
const result = await Promise.race([longRunningOp(), timeout(5000)]);
```
- `Promise.any(promises)` — разрешится при первом выполненном промисе; отклонится (с `AggregateError`), если все промисы отклонятся.
- `Promise.all` — быстрый fail-fast; подходит, когда вам нужны все результаты или нет смысла продолжать при любой ошибке.
4) Практические рекомендации
- Если ошибка одного запроса не должна ломать остальные — используйте `allSettled` или `reflect` и обрабатывайте per-item.
- Чтобы избежать unhandled rejections, всегда поймайте возможные ошибки каждого промиса либо явно обработайте результат `allSettled` / обёртку.
- Для таймаутов и «первого ответа» используйте `race`; для «первого успешного» — `any`.
- Для сбора всех ошибок (например, логирования) используйте `allSettled` и собирайте `reason`.
Если нужно — могу показать конкретный пример для вашей задачи (фоновая загрузка множества URLов, групповой rollback и т. п.).