Promise.all() 與 Promise.allSettled() 的比較
1. 先來了解 Promise.all()
Promise.all() 是 ES6(ECMAScript 2015)就有的方法,它用來處理一個 Promise 的陣列(或可迭代物件)。它的行為是:
-
等待所有 Promise 都成功 resolve:如果陣列中的所有 Promise 都成功完成,它會回傳一個新的 Promise,這個 Promise 會 resolve 成一個陣列,裡面包含每個原始 Promise 的結果(按照陣列順序)。
-
一有 reject 就馬上失敗:如果陣列中任何一個 Promise reject(失敗),Promise.all() 會立即 reject,並且只捕捉到第一個 reject 的原因。其他還沒完成的 Promise 會被忽略(雖然它們可能還在背景執行,但你無法取得它們的結果)。
-
適合情境:當你的任務是「全都要成功」,比如載入多個必要元件,如果其中一個失敗,整個流程就停下來。這就像「全有或全無」的邏輯。
語法:
Promise.all(iterable)
- iterable:一個包含 Promise 的陣列或其他可迭代物件。
2. 再來了解 Promise.allSettled()
Promise.allSettled() 是 ES2020(ECMAScript 2020)新增的方法,它也處理一個 Promise 陣列,但行為不同:
-
等待所有 Promise 都「settle」:這裡的「settle」意思是每個 Promise 都「結束」了,不管是 resolve(成功)還是 reject(失敗)。它永遠會 resolve,不會 reject。
-
回傳詳細結果:回傳的 Promise 會 resolve 成一個陣列,裡面每個元素都是物件,格式是
{ status: 'fulfilled', value: 結果 }或{ status: 'rejected', reason: 錯誤原因 }。這樣你就能知道每個 Promise 的狀態和結果。 -
適合情境:當你想知道「每個任務的完整結果」,即使有些失敗也要繼續執行其他任務。比如,從多個 API 抓資料,有些失敗了,你還是想顯示成功的部分,而不是整個畫面空白。這很適合獨立的非同步任務。
語法:
Promise.allSettled(iterable)
- 同樣,iterable 是包含 Promise 的陣列。
3. 主要差異:用表格來比較
為了讓你更容易看懂,我用表格整理它們的差異(基於 MDN 和其他可靠來源的說明):
| 特點 | Promise.all() | Promise.allSettled() |
|---|---|---|
| 回傳類型 | Promise(resolve 成結果陣列,或 reject 成第一個錯誤) | Promise(永遠 resolve 成狀態物件陣列) |
| 處理 reject | 任何一個 reject 就立即 reject,整個失敗(fail fast) | 等待所有 Promise settle,不管成功或失敗,都會 resolve |
| 結果內容 | 成功時:[結果1, 結果2, ...](純值陣列) 失敗時:只知道第一個錯誤原因 | [ {status: 'fulfilled', value: 結果}, {status: 'rejected', reason: 錯誤} ](詳細狀態) |
| 執行順序 | 並行執行所有 Promise,但一有 reject 就停止等待其他 | 並行執行所有 Promise,全部結束後才 resolve |
| 瀏覽器支援 | ES6,所有現代瀏覽器(Chrome 51+、Firefox 40+ 等) | ES2020,大多數現代瀏覽器(Chrome 76+、Firefox 71+ 等);舊版需 polyfill |
| 使用時機 | 任務互相依賴,全成功才繼續(如載入必要資源) | 任務獨立,想知道每個結果(如多個 API 呼叫) |
4. 完整程式碼範例:讓我們實際操作
現在來寫一個簡單的範例,模擬兩個 Promise:一個成功(延遲 1 秒 resolve '成功'),一個失敗(延遲 500 毫秒 reject '錯誤')。我們會分別用 Promise.all() 和 Promise.allSettled() 處理它們。
步驟 1:建立兩個 Promise 打開瀏覽器的開發者工具(按 F12),在 Console 裡貼上以下程式碼,一行一行輸入或一次貼上執行。
// 建立第一個 Promise:成功,延遲 1 秒後 resolve '成功'
const promise1 = new Promise((resolve) => {
setTimeout(() => {
resolve('成功');
}, 1000); // 1000 毫秒 = 1 秒
});
// 建立第二個 Promise:失敗,延遲 500 毫秒後 reject '錯誤'
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('錯誤');
}, 500); // 500 毫秒 = 0.5 秒
});
// 把兩個 Promise 放進陣列
const promises = [promise1, promise2];
步驟 2:用 Promise.all() 測試 現在執行以下程式碼,看結果:
Promise.all(promises)
.then((results) => {
console.log('所有成功:', results); // 這行不會執行,因為有 reject
})
.catch((error) => {
console.log('有錯誤:', error); // 會輸出 '錯誤',因為 promise2 先 reject
});
-
預期輸出:
有錯誤: 錯誤 -
解釋:因為 promise2 在 0.5 秒就 reject,Promise.all() 馬上失敗,不等 promise1 完成。你只知道「有錯誤」,但不知道其他 Promise 的結果。這就是「fail fast」的優點,但如果失敗的任務不是關鍵,就浪費了。
步驟 3:用 Promise.allSettled() 測試 現在執行以下程式碼:
Promise.allSettled(promises)
.then((results) => {
console.log('所有結果:', results);
// 手動檢查每個狀態
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
console.log(`Promise ${index + 1} 成功:`, result.value);
} else {
console.log(`Promise ${index + 1} 失敗:`, result.reason);
}
});
});
// 注意:沒有 .catch,因為它永遠不會 reject
-
預期輸出(大約 1 秒後):
所有結果: [
{ status: 'fulfilled', value: '成功' },
{ status: 'rejected', reason: '錯誤' }
]
Promise 1 成功: 成功
Promise 2 失敗: 錯誤 -
解釋:它等了 1 秒(等到 promise1 完成),然後給你兩個結果的詳細狀態。即使 promise2 失敗了,你還是得到 promise1 的值。這很適合在前端元件中處理多個獨立請求,比如抓取使用者資料和設定檔,如果設定檔失敗,不影響使用者資料顯示。
5. 實際應用建議
-
在前端專案中使用:假設你在 React 或 Vue 元件中,從多個 API 抓資料。用 Promise.all() 如果所有資料都必要(例如首頁 banner 和推薦商品都不能少);用 Promise.allSettled() 如果想顯示部分資料(例如推薦商品失敗了,還是顯示 banner)。
-
與 async/await 結合:你可以把這些方法包在 async 函式裡,更容易讀:
async function testAllSettled() {
try {
const results = await Promise.allSettled(promises);
console.log(results);
} catch (error) {
// 這邊不會執行,但為了習慣性寫法
}
}
testAllSettled(); -
注意事項:如果你的專案支援舊瀏覽器,Promise.allSettled() 可能需要 polyfill(可以用 core-js 套件)。另外,兩個方法都並行執行 Promise,不會一個接一個等,所以效能好。