iT邦幫忙

2023 iThome 鐵人賽

DAY 16
0
自我挑戰組

JS 加強筆記系列 第 16

Day 16:promise 方法 (1)

  • 分享至 

  • xImage
  •  

Promise.all

使用的情境是有多個 promise 想要同時並行,並等待它們全部的結果。

let promise = Promise.all(iterable);

這裡的參數 iterable 通常會使用元素都是 promise 的陣列。Promise.all 會回傳一個 promise pp 在所有傳入的 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.allSettled

這個方法也是讓多個 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...}
]

上一篇
Day 15:promise 常見問題之 promise constructor anti-pattern
下一篇
Day 17:promise 方法 (2)
系列文
JS 加強筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言