Promise.all()
Статичний метод Promise.all()
(усі) приймає на вхід ітерований об'єкт із промісами й повертає один проміс
. Цей повернений проміс сповнюється, коли сповнено всі вхідні проміси (включно з випадком, коли передана порожня колекція), масивом значень сповнення. Він відхиляється, коли відхиляється будь-який з вихідних промісів, з причиною цього першого відхилення.
Спробуйте його в дії
Синтаксис
Promise.all(iterable)
Параметри
iterable
Ітерований об'єкт (наприклад,
Array
) з промісами.
Повернене значення
Promise
, котрий:
- Одразу сповнений, якщо переданий ітерований об'єкт — порожній.
- Асинхронно сповнений, якщо усі проміси в переданому
iterable
– сповнюються. Значення сповнення є масивом значень сповнення, у порядку передачі промісів, незалежно від порядку їх завершення. Якщо переданеiterable
– непорожнє, але не містить промісів у стані очікування, то повернений проміс все одно сповнюється асинхронно (а не синхронно). - Асинхронно відхилений, якщо будь-який з промісів даного
iterable
відхиляється. Причиною відхилення стає причина відхилення першого відхиленого промісу.
Опис
Метод Promise.all()
– один з методів рівночасності промісів. Він може бути корисним для агрегування результатів кількох промісів. Здебільшого його використовують, коли є кілька взаємопов'язаних асинхронних задач, на котрі в цілому покладається код для своєї успішної роботи – і які всі повинні сповнитись до того, як виконання коду продовжиться.
Promise.all()
відхилиться одразу, як тільки відхилиться будь-який із переданих промісів. У порівнянні з ним проміс, повернений методом Promise.allSettled()
, очікуватиме аж до завершення всіх промісів, незалежно від того, чи відхилиться якийсь із них, чи ні. Використовуйте allSettled()
, якщо потрібний підсумковий результат кожного проміса з вихідної ітерованої колекції.
Приклади
Застосування Promise.all()
Promise.all
очікує на всі сповнення (або першого відхилення).
const p1 = Promise.resolve(3);
const p2 = 1337;
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("foo");
}, 100);
});
Promise.all([p1, p2, p3]).then((values) => {
console.log(values); // [3, 1337, "foo"]
});
Якщо iterable
містить непромісні значення — їх буде проігноровано, проте включено до поверненого промісом масиву значень (за умови, що проміс сповнюється):
// Усі значення – непроміси, тож повернений проміс – сповнюється
const p = Promise.all([1, 2, 3]);
// Єдиний вихідний проміс – відразу сповнений,
// тож повернений проміс сповнюється також
const p2 = Promise.all([1, 2, 3, Promise.resolve(444)]);
// Один (і єдиний) вихідний проміс відхиляється,
// тож повернений проміс відхиляється також
const p3 = Promise.all([1, 2, 3, Promise.reject(555)]);
// За допомогою setTimeout можна виконати код після очищення черги
setTimeout(() => {
console.log(p);
console.log(p2);
console.log(p3);
});
// Друкує:
// Promise { <state>: "fulfilled", <value>: Array[3] }
// Promise { <state>: "fulfilled", <value>: Array[4] }
// Promise { <state>: "rejected", <reason>: 555 }
Асинхронність чи синхронність методу Promise.all
Наступний приклад демонструє асинхронність Promise.all
, коли передано непорожнє значення iterable
:
// Передається масив уже вирішених промісів,
// аби запустити Promise.all якнайшвидше
const resolvedPromisesArray = [Promise.resolve(33), Promise.resolve(44)];
const p = Promise.all(resolvedPromisesArray);
// Негайно друкує значення p
console.log(p);
// За допомогою setTimeout можна виконати код після очищення черги
setTimeout(() => {
console.log("тепер черга порожня");
console.log(p);
});
// Друкує, по порядку:
// Promise { <state>: "pending" }
// тепер черга порожня
// Promise { <state>: "fulfilled", <value>: Array[2] }
Аналогічна ситуація відбуватиметься, якщо Promise.all
відхиляється:
const mixedPromisesArray = [Promise.resolve(33), Promise.reject(44)];
const p = Promise.all(mixedPromisesArray);
console.log(p);
setTimeout(() => {
console.log("тепер черга порожня");
console.log(p);
});
// Друкує
// Promise { <state>: "pending" }
// тепер черга порожня
// Promise { <state>: "rejected", <reason>: 44 }
Проте, Promise.all
вирішується синхронно лише в тому випадку, якщо передано порожній ітерований об'єкт (iterable
):
const p = Promise.all([]); // Вирішується негайно
const p2 = Promise.all([1337, "привіт"]); // Непромісні значення — проігноруються, проте обчислення виконається асинхронно
console.log(p);
console.log(p2);
setTimeout(() => {
console.log("тепер черга порожня");
console.log(p2);
});
// Друкує
// Promise { <state>: "fulfilled", <value>: Array[0] }
// Promise { <state>: "pending" }
// тепер черга порожня
// Promise { <state>: "fulfilled", <value>: Array[2] }
Застосування Promise.all() вкупі з асинхронними функціями
Всередині асинхронних функцій дуже поширена ситуація "переочікування" ("over-await") свого коду. Наприклад, на основі наступних функцій:
function promptForDishChoice() {
return new Promise((resolve, reject) => {
const dialog = document.createElement("dialog");
dialog.innerHTML = `
<form method="dialog">
<p>Чого бажаєте?</p>
<select>
<option value="pizza">Піцу</option>
<option value="pasta">Пасти</option>
<option value="salad">Салату</option>
</select>
<menu>
<li><button value="cancel">Скасувати</button></li>
<li><button type="submit" value="ok">Гаразд</button></li>
</menu>
</form>
`;
dialog.addEventListener("close", () => {
if (dialog.returnValue === "ok") {
resolve(dialog.querySelector("select").value);
} else {
reject(new Error("Користувач скасував діалог"));
}
});
document.body.appendChild(dialog);
dialog.showModal();
});
}
async function fetchPrices() {
const response = await fetch("/prices");
return await response.json();
}
Можна написати таку функцію:
async function getPrice() {
const choice = await promptForDishChoice();
const prices = await fetchPrices();
return prices[choice];
}
Проте зверніть увагу, що виконання promptForDishChoice
і fetchPrices
не залежать від результатів виконання одне одного. Поки користувач обирає свою страву, непогано у фоні отримати ціни, але в коді вище оператор await
змусить асинхронну функцію призупинитися, поки не зроблено вибір, а потім знову – поки не отримані ціни. Можна використати Promise.all
, аби запустити ці операції рівночасно, щоб користувачу не треба було чекати отримання цін для видачі результату:
async function getPrice() {
const [choice, prices] = await Promise.all([
promptForDishChoice(),
fetchPrices(),
]);
return prices[choiceValue];
}
У цьому випадком Promise.all
є найкращим вибором серед методів рівночасності, адже обробка помилок є інтуїтивною: якщо один з промісів відхиляється, то результат стає недоступним, адже ввесь вираз await
викидає помилку.
Promise.all
приймає колекцію промісів, тож якщо він використовується для одночасного виконання декількох асинхронних функцій, слід викликати асинхронні функції та використовувати повернені ними проміси. Безпосередня передача функцій в Promise.all
не працює, адже вони не є промісами.
async function getPrice() {
const [choice, prices] = await Promise.all([
promptForDishChoice,
fetchPrices,
]);
// `choice` і `prices` – ті самі асинхронні функції;
// Promise.all() не робить нічого з непромісами
}
Логіка швидкого провалу методу Promise.all
Promise.all
відхиляється, якщо відхиляється один із переданих елементів. Наприклад, якщо передати до нього чотири проміси, що вирішуються після певної паузи, і один проміс, який негайно відхиляється — Promise.all
буде одразу відхилено.
const p1 = new Promise((resolve, reject) => {
setTimeout(() => resolve("один"), 1000);
});
const p2 = new Promise((resolve, reject) => {
setTimeout(() => resolve("два"), 2000);
});
const p3 = new Promise((resolve, reject) => {
setTimeout(() => resolve("три"), 3000);
});
const p4 = new Promise((resolve, reject) => {
setTimeout(() => resolve("чотири"), 4000);
});
const p5 = new Promise((resolve, reject) => {
reject(new Error("відхилити"));
});
// Застосувавши .catch:
Promise.all([p1, p2, p3, p4, p5])
.then((values) => {
console.log(values);
})
.catch((error) => {
console.error(error.message);
});
// Друкує:
// "reject"
Є можливість змінити цю поведінку, шляхом обробки можливих відхилень:
const p1 = new Promise((resolve, reject) => {
setTimeout(() => resolve("затримане_вирішення_p1"), 1000);
});
const p2 = new Promise((resolve, reject) => {
reject(new Error("негайне_відхилення_p2"));
});
Promise.all([p1.catch((error) => error), p2.catch((error) => error)]).then(
(values) => {
console.log(values[0]); // "затримане_вирішення_p1"
console.error(values[1]); // "Error: негайне_відхилення_p2"
},
);
Специфікації
Сумісність із браузерами
desktop | mobile | server | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
all()
|
Chrome Full support 32 | Edge Full support 12 | Firefox Full support 29 | Internet Explorer No support Ні | Opera Full support 19 | Safari Full support 8 | WebView Android Full support 4.4.3 | Chrome Android Full support 32 | Firefox for Android Full support 29 | Opera Android Full support 19 | Safari on iOS Full support 8 | Samsung Internet Full support 2.0 | Deno Full support 1.0 | Node.js Full support 0.12.0 |