Промисы: комбинаторы
Для работы с несколькими промисами есть четыре метода.
Promise.all
Ждёт все промисы. Возвращает массив результатов в том же порядке. Если хотя бы один упадёт — весь промис упадёт.
const [users, posts, comments] = await Promise.all([
fetch('/api/users').then((r) => r.json()),
fetch('/api/posts').then((r) => r.json()),
fetch('/api/comments').then((r) => r.json()),
]);
Все три запроса идут параллельно. Общее время — самый долгий запрос, а не сумма.
Использование:
Promise.all([p1, p2, p3])
.then(([r1, r2, r3]) => console.log(r1, r2, r3))
.catch((err) => console.error('Один упал:', err));
Promise.allSettled
Ждёт все промисы, даже если некоторые упадут. Возвращает массив объектов со статусом.
const results = await Promise.allSettled([p1, p2, p3]);
// [
// { status: 'fulfilled', value: ... },
// { status: 'rejected', reason: ... },
// { status: 'fulfilled', value: ... }
// ]
Обработка:
results.forEach((result) => {
if (result.status === 'fulfilled') {
console.log('Успех:', result.value);
} else {
console.error('Ошибка:', result.reason);
}
});
Когда использовать: когда нужны все результаты, и падение одного не должно ломать остальные.
Promise.race
Возвращает результат первого завершённого промиса — успешного или упавшего.
const result = await Promise.race([
fetch('/api/fast'),
fetch('/api/slow'),
]);
Использование: таймауты.
function withTimeout(promise, ms) {
return Promise.race([
promise,
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), ms)
),
]);
}
await withTimeout(fetchData(), 5000);
Promise.any
Возвращает первый успешный промис. Игнорирует упавшие.
const result = await Promise.any([
fetch('/api/mirror-1'),
fetch('/api/mirror-2'),
fetch('/api/mirror-3'),
]);
Если все упадут — AggregateError:
try {
const result = await Promise.any([p1, p2]);
} catch (err) {
if (err instanceof AggregateError) {
console.error('Все упали:', err.errors);
}
}
Сравнение
| Метод | Ждёт | Успех | Ошибка |
|---|---|---|---|
all | все | массив | первый reject |
allSettled | все | массив со статусами | никогда |
race | первый | первый | первый reject |
any | первый успешный | первый | все reject |
Последовательные vs параллельные
Последовательно (медленно):
const a = await fetchA();
const b = await fetchB();
const c = await fetchC();
// время = a + b + c
Параллельно (быстро):
const [a, b, c] = await Promise.all([fetchA(), fetchB(), fetchC()]);
// время = max(a, b, c)
Ограничение параллельности
Если запросов сотни — не запускай все сразу:
async function mapLimit(items, limit, fn) {
const results = [];
const executing = new Set();
for (const item of items) {
const p = fn(item).then((r) => {
executing.delete(p);
return r;
});
results.push(p);
executing.add(p);
if (executing.size >= limit) {
await Promise.race(executing);
}
}
return Promise.all(results);
}
Итоги
Promise.all— все, один упал — все упалиallSettled— все, даже с ошибкамиrace— первый завершённыйany— первый успешный- Параллельно быстрее последовательного
- Ограничивай параллельность при нагрузке