iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 27
1
Modern Web

JavaScript 之旅系列 第 27

JavaScript 之旅 (27):Promise.any() & AggregateError

本篇介紹 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'),
]);

資料來源


上一篇
JavaScript 之旅 (26):String.prototype.replaceAll()
下一篇
JavaScript 之旅 (28):Numeric Separators
系列文
JavaScript 之旅30

尚未有邦友留言

立即登入留言