Пошук уроків, статей та іншого контенту
Чотири способи запустити кілька асинхронних операцій паралельно та по-різному обробити їхні результати.
Попередній урок показав, що послідовні await для незалежних операцій витрачають час даремно — кожна чекає завершення попередньої, хоча могла б виконуватись одночасно. Коли кілька Promise не залежать один від одного (наприклад, запити до різних ендпоінтів API), їх варто запускати паралельно й чекати результат разом.
Promise.all приймає масив Promise і повертає новий Promise, що виконується масивом усіх результатів — але лише якщо кожен із них виконався успішно:
async function loadDashboard(userId) {
const [user, orders, notifications] = await Promise.all([
fetchUserData(userId),
fetchOrders(userId),
fetchNotifications(userId),
]);
return { user, orders, notifications };
// усі три запити виконуються ОДНОЧАСНО, а не по черзі — загальний час ≈ найдовшому запиту, а не сумі всіх трьох
}Якщо хоча б один Promise у масиві відхиляється, Promise.all одразу відхиляється весь результат — навіть якщо решта завершились успішно. Результати вже виконаних операцій при цьому втрачаються.
Коли потрібні результати всіх операцій, навіть якщо частина з них провалилась, allSettled ніколи не відхиляється сам — повертає масив об'єктів зі статусом кожного:
const results = await Promise.allSettled([
fetchUserData(1),
fetchUserData(-1), // ця відхилиться
]);
results;
// [
// { status: "fulfilled", value: { id: 1, name: "Олена" } },
// { status: "rejected", reason: Error("Некоректний userId") }
// ]
const succeeded = results.filter((r) => r.status === "fulfilled"); // filter з модуля «Об'єкти та масиви»race виконується (чи відхиляється) щойно завершується перший із переданих Promise:
function timeout(ms) {
return new Promise((_, reject) => setTimeout(() => reject(new Error("Час очікування вичерпано")), ms));
}
const result = await Promise.race([
fetchUserData(1),
timeout(3000), // якщо fetchUserData не встигне за 3 секунди, race відхилиться першим
]);Схожий на race, але ігнорує відхилені Promise, доки не знайде перший успішний (або не відхилиться, якщо провалились абсолютно всі):
const fastestMirror = await Promise.any([
fetch("https://mirror1.example.com/data"),
fetch("https://mirror2.example.com/data"),
]); // перший сервер, що відповів успішно, незалежно від того, чи інші впалиPromise.all — потрібні всі результати; провал будь-кого зупиняє все.
Promise.allSettled — потрібен статус кожного, незалежно від того, скільки провалилось.
Promise.race — потрібен перший результат, байдуже, успіх це чи помилка (типово для тайм-аутів).
Promise.any — потрібен перший успішний результат, помилки окремих спроб ігноруються.
Використання Promise.all для операцій, де провал однієї не має скасовувати результат решти — тут потрібен allSettled.
Послідовні await для незалежних операцій замість Promise.all — марна втрата часу виконання.
Плутанина race і any — race реагує на будь-яке перше завершення (включно з помилкою), any — лише на перший успіх.
Чотири методи Promise для різних сценаріїв паралельного виконання: all — усі мають вдатись, allSettled — потрібен статус кожного, race — перше завершення (успіх чи ні), any — перший успіх. Вибір залежить від того, як має поводитись код, коли частина операцій провалюється.
Спробуйте самостійно
Поясніть своїми словами: чому для завантаження даних дашборду (кілька незалежних віджетів, кожен зі своїм запитом) Promise.allSettled зазвичай кращий вибір за Promise.all?