Promise.all, Promise.race, Promise.allSettled, Promise.any
Статические методы Promise
JavaScript предоставляет четыре основных статических метода для работы с несколькими промисами одновременно:
Promise.all()Promise.race()Promise.allSettled()Promise.any()
Каждый из них решает разные задачи. Разберём подробно.
Promise.all()
Ожидает выполнения всех промисов. Если хотя бы один отклонён — весь результат отклоняется.
Синтаксис
Promise.all(iterable)
Поведение
- ✅ Возвращает массив результатов, если все промисы выполнены успешно
- ❌ Отклоняется с ошибкой первого отклонённого промиса
- Порядок результатов соответствует порядку промисов
Пример успешного выполнения
const promise1 = Promise.resolve(10);
const promise2 = Promise.resolve(20);
const promise3 = Promise.resolve(30);
Promise.all([promise1, promise2, promise3])
.then(results => {
console.log(results); // [10, 20, 30]
});
Пример с отклонением
const promise1 = Promise.resolve(10);
const promise2 = Promise.reject('Error!');
const promise3 = Promise.resolve(30);
Promise.all([promise1, promise2, promise3])
.then(results => {
console.log(results);
})
.catch(error => {
console.log(error); // "Error!"
// promise3 будет проигнорирован
});
Когда использовать?
- Загрузка нескольких независимых ресурсов одновременно
- Все запросы обязательны для продолжения работы
- Нужно дождаться выполнения всех операций
async function loadUserData(userId) {
const [user, posts, comments] = await Promise.all([
fetchUser(userId),
fetchPosts(userId),
fetchComments(userId)
]);
return { user, posts, comments };
}
Важно:
Если один промис отклоняется, остальные промисы продолжают выполняться, но их результаты будут проигнорированы.
Promise.race()
Возвращает результат первого завершившегося промиса (успешно или с ошибкой).
Синтаксис
Promise.race(iterable)
Поведение
- Завершается, как только любой промис завершится
- Возвращает результат или ошибку первого завершённого промиса
- Остальные промисы игнорируются
Пример
const slow = new Promise(resolve => {
setTimeout(() => resolve('Медленный'), 2000);
});
const fast = new Promise(resolve => {
setTimeout(() => resolve('Быстрый'), 500);
});
Promise.race([slow, fast])
.then(result => {
console.log(result); // "Быстрый"
});
Пример с ошибкой
const success = new Promise(resolve => {
setTimeout(() => resolve('Успех'), 2000);
});
const failure = new Promise((resolve, reject) => {
setTimeout(() => reject('Ошибка'), 500);
});
Promise.race([success, failure])
.then(result => console.log(result))
.catch(error => console.log(error)); // "Ошибка"
Когда использовать?
- Таймауты: отменить операцию, если она занимает слишком много времени
- Запасные варианты: попробовать несколько источников данных
- Гонка запросов: использовать самый быстрый ответ
// Таймаут для запроса
function fetchWithTimeout(url, timeout = 5000) {
return Promise.race([
fetch(url),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), timeout)
)
]);
}
fetchWithTimeout('/api/data', 3000)
.then(response => response.json())
.catch(error => console.error(error));
Promise.allSettled()
Ожидает завершения всех промисов (успешно или с ошибкой) и возвращает их статусы.
Синтаксис
Promise.allSettled(iterable)
Поведение
- ✅ Всегда разрешается (никогда не отклоняется)
- Возвращает массив объектов с результатами каждого промиса
- Каждый объект содержит
statusиvalue/reason
Формат результата
[
{ status: 'fulfilled', value: результат },
{ status: 'rejected', reason: ошибка }
]
Пример
const promises = [
Promise.resolve(10),
Promise.reject('Error'),
Promise.resolve(30)
];
Promise.allSettled(promises)
.then(results => {
console.log(results);
/*
[
{ status: 'fulfilled', value: 10 },
{ status: 'rejected', reason: 'Error' },
{ status: 'fulfilled', value: 30 }
]
*/
});
Обработка результатов
const results = await Promise.allSettled([
fetch('/api/users'),
fetch('/api/posts'),
fetch('/api/comments')
]);
const successful = results
.filter(r => r.status === 'fulfilled')
.map(r => r.value);
const failed = results
.filter(r => r.status === 'rejected')
.map(r => r.reason);
console.log('Успешно:', successful.length);
console.log('Неудачно:', failed.length);
Когда использовать?
- Нужны результаты всех операций, даже если некоторые провалились
- Агрегация данных из нескольких источников
- Частичная загрузка данных (показать то, что загрузилось)
async function loadDashboard() {
const [userResult, statsResult, notificationsResult] = await Promise.allSettled([
fetchUser(),
fetchStats(),
fetchNotifications()
]);
return {
user: userResult.status === 'fulfilled' ? userResult.value : null,
stats: statsResult.status === 'fulfilled' ? statsResult.value : null,
notifications: notificationsResult.status === 'fulfilled'
? notificationsResult.value
: []
};
}
ES2020:
Promise.allSettled() был добавлен в ES2020 и поддерживается всеми современными браузерами.
Promise.any()
Возвращает первый успешно выполненный промис. Отклоняется, только если все промисы отклонены.
Синтаксис
Promise.any(iterable)
Поведение
- ✅ Разрешается с результатом первого успешного промиса
- ❌ Отклоняется только если все промисы отклонены (с
AggregateError) - Игнорирует отклонённые промисы, пока есть хотя бы один успешный
Пример
const promises = [
Promise.reject('Ошибка 1'),
Promise.resolve('Успех!'),
Promise.reject('Ошибка 2')
];
Promise.any(promises)
.then(result => {
console.log(result); // "Успех!"
});
Пример с полным отклонением
const promises = [
Promise.reject('Ошибка 1'),
Promise.reject('Ошибка 2'),
Promise.reject('Ошибка 3')
];
Promise.any(promises)
.catch(error => {
console.log(error); // AggregateError: All promises were rejected
console.log(error.errors); // ['Ошибка 1', 'Ошибка 2', 'Ошибка 3']
});
Когда использовать?
- Запасные варианты: попробовать несколько источников данных
- Нужен хотя бы один успешный результат
- Работа с ненадёжными API (пробуем несколько серверов)
// Загрузка изображения с резервных серверов
async function loadImage(imageName) {
const servers = [
`https://cdn1.example.com/${imageName}`,
`https://cdn2.example.com/${imageName}`,
`https://cdn3.example.com/${imageName}`
];
try {
const imageUrl = await Promise.any(
servers.map(url => fetch(url).then(r => {
if (!r.ok) throw new Error('Failed');
return url;
}))
);
return imageUrl;
} catch (error) {
console.error('Все серверы недоступны');
}
}
ES2021:
Promise.any() был добавлен в ES2021.
Сравнительная таблица
| Метод | Завершается когда | Результат успеха | Результат ошибки |
|---|---|---|---|
Promise.all | Все выполнены ИЛИ первый отклонён | Массив всех результатов | Первая ошибка |
Promise.race | Первый завершился | Результат первого | Ошибка первого |
Promise.allSettled | Все завершились | Массив {status, value/reason} | Никогда не отклоняется |
Promise.any | Первый успешный ИЛИ все отклонены | Первый успешный результат | AggregateError |
Практические примеры
Загрузка с таймаутом и retry
async function fetchWithRetry(url, retries = 3, timeout = 5000) {
for (let i = 0; i < retries; i++) {
try {
const result = await Promise.race([
fetch(url),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), timeout)
)
]);
return result;
} catch (error) {
if (i === retries - 1) throw error;
console.log(`Попытка ${i + 1} не удалась, повторяем...`);
}
}
}
Параллельная загрузка с ограничением
async function fetchWithLimit(urls, limit = 3) {
const results = [];
const executing = [];
for (const url of urls) {
const promise = fetch(url).then(r => r.json());
results.push(promise);
if (limit <= urls.length) {
const executing_promise = promise.then(() =>
executing.splice(executing.indexOf(executing_promise), 1)
);
executing.push(executing_promise);
if (executing.length >= limit) {
await Promise.race(executing);
}
}
}
return Promise.all(results);
}
Частичная загрузка данных
async function loadPageData() {
const results = await Promise.allSettled([
fetchCriticalData(), // Обязательные данные
fetchOptionalWidget1(), // Необязательные виджеты
fetchOptionalWidget2(),
fetchOptionalWidget3()
]);
// Если критические данные не загрузились - показываем ошибку
if (results[0].status === 'rejected') {
throw new Error('Не удалось загрузить страницу');
}
return {
critical: results[0].value,
widgets: results.slice(1)
.filter(r => r.status === 'fulfilled')
.map(r => r.value)
};
}
Вывод
Promise.all()— все или ничего (параллельная загрузка обязательных данных)Promise.race()— побеждает первый (таймауты, гонка запросов)Promise.allSettled()— результат каждого (частичная загрузка, агрегация)Promise.any()— первый успешный (резервные серверы, fallback)
На собеседовании:
Частые вопросы:
- Чем отличается
Promise.allотPromise.allSettled? - Когда использовать
Promise.racevsPromise.any? - Что произойдёт, если передать пустой массив в каждый метод?
- Как обработать частичные ошибки при загрузке данных?