本篇介紹 ES2021 (ES12) 提供的 Promise.any()
和 AggregateError
。
之前有介紹 ES2020 (ES11) 提供的 Promise.allSettled()
,它不會發生短路,也就是都會等所有傳入的 Promise settled (即 fulfilled 或 rejected ),而本篇介紹的 Promise.any()
則是會因其中一個傳入的 Promise fulfilled 而發生短路。
例如:在 Promise.any()
傳入 3 的 Promise,只有第一個會立即 rejected,其餘的都會立即 fulfilled:
let promise = Promise.any([
Promise.reject('Oops 1'),
Promise.resolve('OK 1'),
Promise.resolve('OK 2')
]);
可以看到最後回傳的 Promise 會 fulfilled,因為 Promise.any()
只要其中一個 Promise fulfilled 就會發生短路,即會立即回傳該 Promise fulfilled:
console.log(promise);
// Promise {<fulfilled>: "OK 1"}
若所有傳入的 Promise 都 rejected,則會以 AggregateError
rejected,並會保留所有 rejection reasons (即下面範例中的 error.errors
):
Promise.any([
Promise.reject('Oops 1'),
Promise.reject('Oops 2'),
Promise.reject('Oops 3')
])
.catch(error => {
console.log(error instanceof AggregateError);
console.log(error.errors);
console.log(error.message);
console.log(error.stack);
});
// true
// ["Oops 1", "Oops 2", "Oops 3"]
// All promises were rejected
// AggregateError: All promises were rejected
另外,你也可以自行建立新的 AggregateError
物件:
// 語法:AggregateError(errors, message)
new AggregateError([errorA, errorB, errorC], 'error message');
Promise.any()
很適合用在一次處理多個非同步,並且抓出第一個 fulfilled 的 Promise,只有當全部都 rejected 才進行錯誤處理。
例如:同時發多個 request,看哪一個 endpoint 回應最快,然後把該 endpoint 紀錄下來。而且只有當所有 request 都發失敗時,才會進行錯誤處理:
let Base_URL = 'https://jsonplaceholder.typicode.com';
let promises = [
fetch(`${Base_URL}/posts/1`).then(() => 'post'),
fetch(`${Base_URL}/todos/1`).then(() => 'todo'),
fetch(`${Base_URL}/comments/1`).then(() => 'comment')
];
try {
const first = await Promise.any(promises);
console.log(first);
// 紀錄 log...
// Logger.log(first);
} catch (error) {
console.log(error.errors);
// 錯誤處理...
}
另一個適合的情境是你想動態 import 一個模組,但有兩個來源可以 import,但你只需 import 最快的那一個即可,此時也很適合用 Promise.any()
:
const lodash = await Promise.any([
import('https://primary.example.com/lodash'),
import('https://secondary.example.com/lodash'),
]);
Promise.any()
| 2ality