Что такое Promise.all и Promise.allSettled в JavaScript?
Promise.all
Promise.all принимает итерируемый объект (обычно массив) промисов и возвращает новый промис, который:
- Разрешается, когда все переданные промисы успешно завершились. Результатом будет массив значений в том же порядке, что и входные промисы.
- Отклоняется немедленно, как только хотя бы один из промисов отклоняется. Остальные промисы при этом не отменяются, но их результаты игнорируются.
Это поведение называют «fail-fast»: один неуспех прерывает всю операцию.
const [user, posts] = await Promise.all([
fetchUser(1),
fetchPosts(1)
]);
// Если fetchPosts упадёт — весь Promise.all отклонится
Когда использовать Promise.all
- Когда все результаты необходимы для продолжения работы.
- Когда частичный результат не имеет смысла (транзакции, связанные данные).
- Когда нужна максимальная производительность: все задачи стартуют одновременно.
Promise.allSettled
Promise.allSettled также принимает массив промисов, но ведёт себя иначе:
- Всегда разрешается (никогда не отклоняется) после того, как все промисы завершились — успешно или нет.
- Возвращает массив объектов-дескрипторов с полем
status: 'fulfilled' | 'rejected'.- При успехе:
{ status: 'fulfilled', value: результат } - При ошибке:
{ status: 'rejected', reason: ошибка }
- При успехе:
const results = await Promise.allSettled([
fetchUser(1),
fetchPosts(1),
fetchComments(1)
]);
results.forEach(result => {
if (result.status === 'fulfilled') {
console.log('Успех:', result.value);
} else {
console.error('Ошибка:', result.reason);
}
});
Когда использовать Promise.allSettled
- Когда нужно обработать все результаты, даже если часть запросов упала.
- Независимые операции: сохранение аналитики, отправка уведомлений нескольким сервисам.
- Для сбора диагностики: какие из N операций прошли, а какие нет.
Сравнение на практике
| Promise.all | Promise.allSettled | |
|---|---|---|
| Отклоняется при ошибке | Да, немедленно | Нет, всегда завершается |
| Возвращает | Массив значений | Массив дескрипторов |
| Введён в | ES2015 | ES2020 |
| Подход к ошибкам | Fail-fast | Collect-all |
Частые паттерны
Фильтрация успешных результатов из allSettled
const results = await Promise.allSettled(promises);
const fulfilled = results
.filter((r): r is PromiseFulfilledResult<User> => r.status === 'fulfilled')
.map(r => r.value);
Параллельная загрузка с обязательными и необязательными данными
// Критичные данные — через Promise.all
const [user, config] = await Promise.all([getUser(), getConfig()]);
// Необязательные данные — через Promise.allSettled
const extras = await Promise.allSettled([getRecommendations(), getAds()]);
Что хочет услышать интервьюер
Кандидат чётко объясняет разницу в поведении при ошибке: Promise.all отклоняется сразу, Promise.allSettled ждёт всех
Знание структуры возвращаемых значений: Promise.all возвращает массив значений, Promise.allSettled — массив объектов со status/value/reason
Понимание, что оба метода запускают промисы параллельно, а не последовательно
Умение выбрать подходящий метод под конкретную задачу (fail-fast vs collect-all)
Знание версий ES: Promise.all — ES2015, Promise.allSettled — ES2020
Пример: Promise.all — параллельная загрузка взаимозависимых данных
interface User { id: number; name: string; }
interface Post { id: number; title: string; }
async function fetchUser(id: number): Promise<User> {
const res = await fetch(`/api/users/${id}`);
if (!res.ok) throw new Error('Пользователь не найден');
return res.json();
}
async function fetchPosts(userId: number): Promise<Post[]> {
const res = await fetch(`/api/posts?userId=${userId}`);
if (!res.ok) throw new Error('Посты не найдены');
return res.json();
}
async function loadUserProfile(userId: number) {
try {
// Оба запроса стартуют одновременно
const [user, posts] = await Promise.all([
fetchUser(userId),
fetchPosts(userId)
]);
console.log(`Профиль: ${user.name}, постов: ${posts.length}`);
return { user, posts };
} catch (error) {
// Любая ошибка прерывает операцию
console.error('Не удалось загрузить профиль:', error);
throw error;
}
}
Пример: Promise.allSettled — независимые операции с обработкой каждого результата
interface NotificationResult {
service: string;
success: boolean;
error?: string;
}
async function sendEmail(userId: number): Promise<void> {
// Отправка email
}
async function sendPush(userId: number): Promise<void> {
// Отправка push-уведомления
}
async function sendSms(userId: number): Promise<void> {
// Отправка SMS
}
async function notifyUser(userId: number): Promise<NotificationResult[]> {
const services = [
{ name: 'email', fn: () => sendEmail(userId) },
{ name: 'push', fn: () => sendPush(userId) },
{ name: 'sms', fn: () => sendSms(userId) },
];
const results = await Promise.allSettled(services.map(s => s.fn()));
// Promise.allSettled всегда разрешается — собираем отчёт
return results.map((result, i) => ({
service: services[i].name,
success: result.status === 'fulfilled',
error: result.status === 'rejected'
? String(result.reason)
: undefined,
}));
}
// Пример использования
const report = await notifyUser(42);
const failed = report.filter(r => !r.success);
if (failed.length > 0) {
console.warn('Не доставлено через:', failed.map(r => r.service).join(', '));
}
Типичные ошибки
Считают, что Promise.all отменяет остальные промисы при ошибке — на самом деле они продолжают выполняться, просто результат игнорируется
Путают порядок результатов: думают, что он зависит от скорости выполнения, а не от порядка входного массива
Забывают, что Promise.allSettled никогда не переходит в rejected — оборачивают вызов в try/catch без необходимости
Не проверяют поле status в результатах Promise.allSettled и обращаются к value напрямую, не учитывая rejected-случаи
Используют Promise.allSettled там, где нужен fail-fast — например, при загрузке взаимозависимых данных


