使用的情境是有多個 promise 想要同時並行,並等待它們全部的結果。
let promise = Promise.all(iterable);
這裡的參數 iterable
通常會使用元素都是 promise 的陣列。Promise.all
會回傳一個 promise p
,p
在所有傳入的 promise 都 resolve 後,會以包含所有結果的陣列 resolve。
以下的 Promise.all
會在 3 秒後 resolve,結果為 [1, 2, 3]。結果陣列中元素的順序會對應一開始傳入的 promise 順序,不會受它們執行的時間影響,所以第一個 promise 雖然最晚才 resolve,它的結果仍然會是第一個元素。
Promise.all([
new Promise(resolve => setTimeout(() => resolve(1), 3000)), // 1
new Promise(resolve => setTimeout(() => resolve(2), 2000)), // 2
new Promise(resolve => setTimeout(() => resolve(3), 1000)) // 3
]).then((result) => {
console.log(result); // [1, 2, 3]
});
也因為傳入陣列這點,實務上常會採用的小技巧是使用 map()
方法將一個陣列的資料轉為一個陣列的 promise 再放進 Promise.all
裡執行:
let urls = [
'https://api.github.com/users/iliakan',
'https://api.github.com/users/remy',
'https://api.github.com/users/jeresig'
];
// 將每個一 url 變成請求 promise
let requests = urls.map(url => fetch(url));
Promise.all(requests)
.then(responses => responses.forEach(
response => alert(`${response.url}: ${response.status}`)
));
如果任何一個 promise 變為 rejected,Promise.all
回傳的 promise 也會以它的錯誤 reject。也就是說只要有一個失敗,其他成功 promise 的結果會被忽略。
例如以下第二個 promise 失敗,所以造成整個 Promise.all
失敗:
Promise.all([
new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)),
new Promise((resolve, reject) => setTimeout(() => reject(new Error("Whoops!")), 2000)),
new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000))
]).catch(alert); // Error: Whoops!
另外,雖然可能不常見,但如果傳入的 iterable (陣列) 的元素為純值而非 promise,會維持原本的值作為結果。
Promise.all([
new Promise((resolve, reject) => {
setTimeout(() => resolve(1), 1000)
}),
2,
3
]).then(alert); // 1, 2, 3
這個方法也是讓多個 promise 同時並行。跟 Promise.all
差別在於 Promise.all
只要有一個 promise 失敗就會失敗,Promise.allSettled
則是會等待所有的結果。
也因為會得到所有成功或失敗的結果,每個 promise 的結果會是以下的格式:
{status:"fulfilled", value:result} // 成功時的結果
{status:"rejected", reason:error} // 失敗時的錯誤
舉例來說:
let urls = [
'https://api.github.com/users/iliakan',
'https://api.github.com/users/remy',
'https://no-such-url'
];
Promise.allSettled(urls.map(url => fetch(url)))
.then(results => { // 第 8 行
results.forEach((result, num) => {
if (result.status == "fulfilled") {
alert(`${urls[num]}: ${result.value.status}`);
}
if (result.status == "rejected") {
alert(`${urls[num]}: ${result.reason}`);
}
});
});
第 8 行得到的結果會是如下,可以再針對狀態作進一步處理:
[
{status: 'fulfilled', value: ...response...},
{status: 'fulfilled', value: ...response...},
{status: 'rejected', reason: ...error object...}
]