為什么 Promise.all 不安全?并發處理的新選擇
無論是同時請求多個API、并行處理多個文件,還是執行一系列獨立的動畫,我們都需要一個可靠的機制來管理這些任務。
長久以來,Promise.all 一直是我們的首選方案,但它有一個致命的弱點。

Promise.all
讓我們來看一個例子,假設我們需要從三個不同的API獲取數據來渲染一個頁面儀表盤:
const api1 = fetch('/api/user-info');
const api2 = fetch('/api/dashboard-widgets');
const api3 = fetch('/api/notifications');
Promise.all([api1, api2, api3])
.then(([userInfo, widgets, notifications]) => {
// 當三個請求都成功時,我們在這里處理數據
console.log('所有數據加載成功!');
renderDashboard(userInfo, widgets, notifications);
})
.catch(error => {
// 只要有一個請求失敗,就會立即進入這里
console.error('加載失敗:', error);
showErrorUI();
});想象一下,獲取用戶信息的 api1 和獲取小組件的 api2 都成功了,但獲取通知的 api3 因為服務器打了個盹而失敗了。
整個操作因為一個非核心的通知功能失敗而全盤崩潰,這顯然不是我們想要的用戶體驗。
這就是 Promise.all 的核心風險:它不關心那些已經成功的 Promise,一旦有失敗,就會丟失所有結果。
Promise.allSettled
為了解決上述問題,ECMAScript 引入了 Promise.allSettled,它的設計哲學與 Promise.all 完全不同,它是一個更加寬容和穩健的并發處理器。
Promise.allSettled 同樣接收一個 Promise 數組,但它的行為是:
- 其返回的 Promise 永遠不會被拒絕,無論是成功還是失敗
- 其返回的 Promise 其解析值是一個對象數組,每個對象都描述了對應 Promise 的最終狀態
每個結果對象都有以下兩種形態之一:
- { status: 'fulfilled', value: <解析值> }
- { status: 'rejected', reason: <拒絕原因> }
讓我們用 Promise.allSettled 重寫上面的例子:
const api1 = fetch('/api/user-info');
const api2 = fetch('/api/dashboard-widgets');
const api3 = fetch('/api/notifications'); // 假設這個會失敗
Promise.allSettled([api1, api2, api3])
.then(results => {
// 我們可以安全地處理每一個結果,假設 api3 失敗
if (notificationsResult[2].status === 'rejected') {
console.warn('通知加載失敗:', notificationsResult[2].reason);
// 即使通知失敗,其他部分依然可以正常渲染
showNotificationFallback();
}
});
// 注意:這里幾乎不需要 .catch(),因為它永遠是 fulfilled 狀態通過 Promise.allSettled,即使 api3 失敗了,我們依然能夠拿到 api1 和 api2 的成功結果,并分別進行處理,應用的健壯性大大提高。
Promise.allSettled 通過提供一種永不失敗的承諾聚合方式,讓我們能夠以一種更精細、更安全的方式來處理并發任務。






























