自 ES2015 引入 Promise 以來,JavaScript 僅支援兩種 Promise 組合器:靜態方法 Promise.all
和 Promise.race
。
目前有兩個新提案正在標準化過程中:Promise.allSettled
和 Promise.any
。有了這些新增功能,JavaScript 中將共有四種 Promise 組合器,每種都能啟用不同的使用案例。
以下是四種組合器的概觀
名稱 | 說明 | 狀態 |
---|---|---|
Promise.allSettled | 不會短路 | 已新增至 ES2020 ✅ |
Promise.all | 當輸入值遭到拒絕時會短路 | 已新增至 ES2015 ✅ |
Promise.race | 當輸入值已解決時會短路 | 已新增至 ES2015 ✅ |
Promise.any | 當輸入值已完成時會短路 | 已新增至 ES2021 ✅ |
讓我們來看每個組合器的範例使用案例。
Promise.all
#
- Chrome: 自版本 32 起支援
- Firefox: 自版本 29 起支援
- Safari: 自版本 8 起支援
- Node.js: 自版本 0.12 起支援
- Babel: 支援
Promise.all
讓您知道所有輸入 Promise 是否已完成,或其中一個遭到拒絕。
想像一下使用者按下按鈕,而您想要載入一些樣式表,以便您可以呈現全新的 UI。這個程式會同時針對每個樣式表啟動 HTTP 要求
const promises = [
fetch('/component-a.css'),
fetch('/component-b.css'),
fetch('/component-c.css'),
];
try {
const styleResponses = await Promise.all(promises);
enableStyles(styleResponses);
renderNewUi();
} catch (reason) {
displayError(reason);
}
您只想要在所有要求都成功後才開始呈現新的 UI。如果發生問題,您想要在不等待其他任何工作完成的情況下,盡快顯示錯誤訊息。
在這種情況下,您可以使用 Promise.all
:您想要知道所有 Promise 何時完成,或其中一個遭到拒絕時。
Promise.race
#
- Chrome: 自版本 32 起支援
- Firefox: 自版本 29 起支援
- Safari: 自版本 8 起支援
- Node.js: 自版本 0.12 起支援
- Babel: 支援
如果您想要執行多個 Promise,而且…
- 在第一個成功的結果出現時執行某些動作(如果其中一個 Promise 已完成),或
- 在其中一個 Promise 遭到拒絕時立即執行某些動作。
也就是說,如果其中一個 Promise 遭到拒絕,您希望保留該拒絕以分別處理錯誤案例。以下範例執行確切的動作
try {
const result = await Promise.race([
performHeavyComputation(),
rejectAfterTimeout(2000),
]);
renderResult(result);
} catch (error) {
renderError(error);
}
我們啟動一項可能需要很長時間的計算密集型工作,但我們會與在 2 秒後遭到拒絕的 Promise 競爭。根據第一個完成或遭到拒絕的 Promise,我們會在兩個不同的程式碼路徑中呈現計算結果或錯誤訊息。
Promise.allSettled
#
- Chrome: 自版本 76 起支援
- Firefox: 自版本 71 起支援
- Safari: 自版本 13 起支援
- Node.js: 自版本 12.9.0 起支援
- Babel: 支援
Promise.allSettled
會在所有輸入 Promise 完成時提供訊號,表示它們已完成或遭到拒絕。這在您不關心 Promise 狀態,而只想知道工作是否完成的情況下很有用,無論是否成功。
例如,您可以啟動一系列獨立的 API 呼叫,並使用 Promise.allSettled
確保它們在執行其他動作(例如移除載入指示器)之前全部完成
const promises = [
fetch('/api-call-1'),
fetch('/api-call-2'),
fetch('/api-call-3'),
];
// Imagine some of these requests fail, and some succeed.
await Promise.allSettled(promises);
// All API calls have finished (either failed or succeeded).
removeLoadingIndicator();
Promise.any
#
- Chrome: 自版本 85 起支援
- Firefox: 自版本 79 起支援
- Safari: 自版本 14 起支援
- Node.js: 自版本 16 起支援
- Babel: 支援
Promise.any
會在其中一個 Promise 完成時立即提供訊號。這類似於 Promise.race
,但 any
其中一個 Promise 遭到拒絕時不會提早拒絕。
const promises = [
fetch('/endpoint-a').then(() => 'a'),
fetch('/endpoint-b').then(() => 'b'),
fetch('/endpoint-c').then(() => 'c'),
];
try {
const first = await Promise.any(promises);
// Any of the promises was fulfilled.
console.log(first);
// → e.g. 'b'
} catch (error) {
// All of the promises were rejected.
console.assert(error instanceof AggregateError);
// Log the rejection values:
console.log(error.errors);
// → [
// <TypeError: Failed to fetch /endpoint-a>,
// <TypeError: Failed to fetch /endpoint-b>,
// <TypeError: Failed to fetch /endpoint-c>
// ]
}
此程式碼範例會檢查哪個端點回應得最快,然後記錄它。只有在所有要求都失敗時,我們才會進入 catch
區塊,然後我們可以在其中處理錯誤。
Promise.any
的拒絕可以一次代表多個錯誤。為了在語言層級支援這一點,引入了稱為 AggregateError
的新錯誤類型。除了在上述範例中的基本用法之外,AggregateError
物件也可以像其他錯誤類型一樣以程式化方式建構
const aggregateError = new AggregateError([errorA, errorB, errorC], 'Stuff went wrong!');