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('Slow'), 2000);
});
const fast = new Promise(resolve => {
setTimeout(() => resolve('Fast'), 500);
});
Promise.race([slow, fast])
.then(result => {
console.log(result); // "Fast"
});Приклад помилки
const success = new Promise(resolve => {
setTimeout(() => resolve('Success'), 2000);
});
const failure = new Promise((resolve, reject) => {
setTimeout(() => reject('Error'), 500);
});
Promise.race([success, failure])
.then(result => console.log(result))
.catch(error => console.log(error)); // "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: result },
{ status: 'rejected', reason: error }
]Приклад
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('Error 1'),
Promise.resolve('Success!'),
Promise.reject('Error 2')
];
Promise.any(promises)
.then(result => {
console.log(result); // "Success!"
});Приклад повного відхилення
const promises = [
Promise.reject('Error 1'),
Promise.reject('Error 2'),
Promise.reject('Error 3')
];
Promise.any(promises)
.catch(error => {
console.log(error); // AggregateError: All promises were rejected
console.log(error.errors); // ['Error 1', 'Error 2', 'Error 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 |
Практичні приклади
Завантаження з таймаутом і повтором
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()— перший успішний (резервні сервери, резервні варіанти)
На співбесідах:
Звичайні запитання:
- У чому різниця між
Promise.allіPromise.allSettled? - Коли використовувати
Promise.raceпротиPromise.any? - Що станеться, якщо передати порожній масив кожному методу?
- Як обробляти часткові помилки при завантаженні даних?
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.