Promise.all()
Promise.all() 靜態方法接受一個 promises 的可迭代物件作為輸入,並返回一個單獨的 Promise。當所有輸入的 promises 都 fulfilled 時(包括傳入空可迭代物件時),這個返回的 promise 也會 fulfilled,並帶有一個包含所有 fulfillment 值的陣列。如果輸入的 promises 中有任何一個 rejected,它就會 rejected,並帶上第一個 rejection 的原因。
試一試
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 100, "foo");
});
Promise.all([promise1, promise2, promise3]).then((values) => {
console.log(values);
});
// Expected output: Array [3, 42, "foo"]
語法
Promise.all(iterable)
引數
返回值
一個 Promise,它
- 如果傳入的
iterable為空,則已 fulfilled。 - 當給定
iterable中的所有 promises 都 fulfilled 時,則非同步 fulfilled。fulfillment 值是一個 fulfillment 值的陣列,順序與傳入 promises 的順序相同,與完成順序無關。如果傳入的iterable非空但沒有待定的 promises,返回的 promise 仍然會非同步(而不是同步)fulfilled。 - 當給定
iterable中的任何 promise rejected 時,則非同步 rejected。rejection 的原因是第一個被 rejected 的 promise 的 rejection 原因。
描述
Promise.all() 方法是 promise 併發方法之一。它對於聚合多個 promises 的結果非常有用。它通常用於當有多個相關的非同步任務,且整體程式碼需要依賴所有任務成功完成才能繼續執行的情況——我們希望所有這些任務都在程式碼繼續執行之前 fulfilled。
Promise.all() 會在輸入 promises 中有任何一個 rejected 時立即 rejected。相比之下,Promise.allSettled() 返回的 promise 會等待所有輸入 promises 完成,無論是否有 rejected。如果你需要輸入可迭代物件中每個 promise 的最終結果,請使用 allSettled()。
示例
使用 Promise.all()
Promise.all 等待所有 fulfillment(或第一個 rejection)。
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 包含非 promise 值,它們將被忽略,但仍然計入返回的 promise 陣列值中(如果 promise fulfilled)。
// All values are non-promises, so the returned promise gets fulfilled
const p = Promise.all([1, 2, 3]);
// The only input promise is already fulfilled,
// so the returned promise gets fulfilled
const p2 = Promise.all([1, 2, 3, Promise.resolve(444)]);
// One (and the only) input promise is rejected,
// so the returned promise gets rejected
const p3 = Promise.all([1, 2, 3, Promise.reject(new Error("bad"))]);
// Using setTimeout, we can execute code after the queue is empty
setTimeout(() => {
console.log(p);
console.log(p2);
console.log(p3);
});
// Logs:
// Promise { <state>: "fulfilled", <value>: Array[3] }
// Promise { <state>: "fulfilled", <value>: Array[4] }
// Promise { <state>: "rejected", <reason>: Error: bad }
解構結果
如果你正在批次處理已知數量的任務,你會發現解構非常有用。
// With then()
Promise.all([p1, p2, p3]).then(([a, b, c]) => {
console.log(a, b, c); // 3 1337 "foo"
});
// With await
const [a, b, c] = await Promise.all([p1, p2, p3]);
請注意:如果原始 promises 和結果變數的順序不匹配,你可能會遇到微妙的 bug。
Promise.all 的非同步性或同步性
以下示例演示了當傳入非空 iterable 時 Promise.all 的非同步性
// Passing an array of promises that are already resolved,
// to trigger Promise.all as soon as possible
const resolvedPromisesArray = [Promise.resolve(33), Promise.resolve(44)];
const p = Promise.all(resolvedPromisesArray);
// Immediately logging the value of p
console.log(p);
// Using setTimeout, we can execute code after the queue is empty
setTimeout(() => {
console.log("the queue is now empty");
console.log(p);
});
// Logs, in order:
// Promise { <state>: "pending" }
// the queue is now empty
// Promise { <state>: "fulfilled", <value>: Array[2] }
如果 Promise.all rejected,也會發生同樣的事情
const mixedPromisesArray = [
Promise.resolve(33),
Promise.reject(new Error("bad")),
];
const p = Promise.all(mixedPromisesArray);
console.log(p);
setTimeout(() => {
console.log("the queue is now empty");
console.log(p);
});
// Logs:
// Promise { <state>: "pending" }
// the queue is now empty
// Promise { <state>: "rejected", <reason>: Error: bad }
Promise.all 僅當傳入的 iterable 為空時才會同步 resolve
const p = Promise.all([]); // Will be immediately resolved
const p2 = Promise.all([1337, "hi"]); // Non-promise values are ignored, but the evaluation is done asynchronously
console.log(p);
console.log(p2);
setTimeout(() => {
console.log("the queue is now empty");
console.log(p2);
});
// Logs:
// Promise { <state>: "fulfilled", <value>: Array[0] }
// Promise { <state>: "pending" }
// the queue is now empty
// Promise { <state>: "fulfilled", <value>: Array[2] }
將 Promise.all() 與 async 函式一起使用
在 async 函式中,非常常見地會“過度 await”你的程式碼。例如,給定以下函式
function promptForDishChoice() {
return new Promise((resolve, reject) => {
const dialog = document.createElement("dialog");
dialog.innerHTML = `
<form method="dialog">
<p>What would you like to eat?</p>
<select>
<option value="pizza">Pizza</option>
<option value="pasta">Pasta</option>
<option value="salad">Salad</option>
</select>
<menu>
<li><button value="cancel">Cancel</button></li>
<li><button type="submit" value="ok">OK</button></li>
</menu>
</form>
`;
dialog.addEventListener("close", () => {
if (dialog.returnValue === "ok") {
resolve(dialog.querySelector("select").value);
} else {
reject(new Error("User cancelled dialog"));
}
});
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 運算子會導致 async 函式暫停,直到做出選擇,然後再次暫停直到價格獲取完成。我們可以使用 Promise.all 讓它們併發執行,這樣使用者就不必在給出結果之前等待價格獲取完成
async function getPrice() {
const [choice, prices] = await Promise.all([
promptForDishChoice(),
fetchPrices(),
]);
return prices[choice];
}
Promise.all 是這裡併發方法的最佳選擇,因為錯誤處理很直觀——如果任何 promises rejected,結果將不再可用,因此整個 await 表示式都會丟擲錯誤。
Promise.all 接受一個 promises 的可迭代物件,因此如果你使用它來併發執行多個 async 函式,你需要呼叫 async 函式並使用返回的 promises。直接將函式傳遞給 Promise.all 不起作用,因為它們不是 promises。
async function getPrice() {
const [choice, prices] = await Promise.all([
promptForDishChoice,
fetchPrices,
]);
// `choice` and `prices` are still the original async functions;
// Promise.all() does nothing to non-promises
}
Promise.all 快速失敗行為
如果任何元素被 rejected,Promise.all 就會被 rejected。例如,如果你傳入四個在超時後 resolve 的 promises 和一個立即 rejected 的 promise,那麼 Promise.all 將立即 rejected。
const p1 = new Promise((resolve, reject) => {
setTimeout(() => resolve("one"), 1000);
});
const p2 = new Promise((resolve, reject) => {
setTimeout(() => resolve("two"), 2000);
});
const p3 = new Promise((resolve, reject) => {
setTimeout(() => resolve("three"), 3000);
});
const p4 = new Promise((resolve, reject) => {
setTimeout(() => resolve("four"), 4000);
});
const p5 = new Promise((resolve, reject) => {
reject(new Error("reject"));
});
// Using .catch:
Promise.all([p1, p2, p3, p4, p5])
.then((values) => {
console.log(values);
})
.catch((error) => {
console.error(error.message);
});
// Logs:
// "reject"
可以透過處理可能的 rejection 來改變這種行為
const p1 = new Promise((resolve, reject) => {
setTimeout(() => resolve("p1_delayed_resolution"), 1000);
});
const p2 = new Promise((resolve, reject) => {
reject(new Error("p2_immediate_rejection"));
});
Promise.all([p1.catch((error) => error), p2.catch((error) => error)]).then(
(values) => {
console.log(values[0]); // "p1_delayed_resolution"
console.error(values[1]); // "Error: p2_immediate_rejection"
},
);
規範
| 規範 |
|---|
| ECMAScript® 2026 語言規範 # sec-promise.all |
瀏覽器相容性
載入中…