[译]如何使用Promise.any()

784 阅读3分钟

[译]如何使用Promise.any()

原文链接:dmitripavlutin.com/promise-any…


翻译的一些说明:

Promise是一个状态机,有三种状态

  • Pending 进行中
  • Fulfilled resolve之后进行这种状态
  • Rejected reject之后进入这种状态

状态变迁流程如下:

image-20210924101956684

译文中会把Fulfilled翻译为完成,把Rejected翻译为被拒绝


Promise.any(promises)是一个帮助函数,用于并行执行多个promise请求并返回第一个成功resolve的promise的值。

让我们看一下Promise.any() 是如何工作的

1. Promise.any()

Promise.any()对于以并行和竞赛的方式执行独立的异步操作是很有用的,可以获得任何第一个完成(fulfilled)的promise的值。

该函数接受一个promise列表(或者是可迭代对象)作为参数

const anyPromise = Promise.any(promises);

当promise列表中的任何一个promise完成时,anyPromise就会立即resolve为该promise的值。

你可以使用then语法获取第一个promise的值

anyPromise.then(firstValue => {
 firstValue; // 第一个fullfilled的promise的值
});

或者使用 async/await :

const firstValue = await anyPromise;

firstValue; //  第一个fullfilled的promise的值

Promise.any()返回的promise与第一个完成请求的promise一起完成(fulfilled),即便是有的promise请求失败了(rejected)也会被忽略。

如果所有的promise都失败了或者输入的promise数组为空,Promise.any() 会被拒绝(reject)并抛出一个aggregate error,错误中包含所有的promise请求的错误信息。

  1. 举个栗子-Fruits and vegetables

在使用Promise.any()之前,让我们定义两个帮助函数。

第一个, resolveTimeout(value, delay)——在delay毫秒之后返回一个fulfilled的promise,把传递的value参数作为resolve的参数。

function resolveTimeout(value, delay) {
  return new Promise(
    resolve => setTimeout(() => resolve(value), delay)
  );
}

第二个, rejectTimeout(reason, delay) —在delay毫秒之后返回一个被reject的promise,并把参数reason作为错误信息填入。

function rejectTimeout(reason, delay) {
  return new Promise(
    (r, reject) => setTimeout(() => reject(reason), delay)
  );
}

让我们使用这些帮助函数来测试下 Promise.any()

2.1所有的promise都 fulfilled

例子:从商店中获取购买的清单:

const promise = Promise.any([
  resolveTimeout(['potatoes', 'tomatoes'], 1000),  resolveTimeout(['oranges', 'apples'], 2000)]);

// wait...
const list = await promise;

// after 1 second
console.log(list); // logs ['potatoes', 'tomatoes']

Try the demo.

Promise.any([...]) 在1秒之后返回蔬菜列表['potatoes', 'tomatoes'],原因是这个promise首先完成(fulfilled)。

第二个promise在2秒之后完成,但是它的值会被忽略。

2.2 一个Promise被拒绝

想象一个场景:商店中没有蔬菜(vegetables),vegetables的promise就会被拒绝,这种情况下Promise.any()会怎么返回呢?

const promise = Promise.any([
  rejectTimeout(new Error("Out of vegetables!"), 1000),  resolveTimeout(["oranges", "apples"], 2000)
]);

// wait...
const list = await promise;

// after 2 seconds
console.log(list); // logs ['oranges', 'apples']

Try the demo.

这种情况有点棘手。

首先, vegetables promise 在1秒后被拒绝。但是在这种情况下, Promise.any()会跳过这个promise并继续等待下一个promise的结果。

最终,2秒之后, fruits promise完成并返回对应的值 ['oranges', 'apples']。返回的值同样也是Promise.any([...])被resolve时得到的值。

2.3 所有的 promises 都被拒绝

如果商店中蔬菜和水果都没有,即这两个promise都被拒绝的场景,会发生什么?

const promise = Promise.any([
  rejectTimeout(new Error('Out of vegetables!'), 1000),  rejectTimeout(new Error('Out of fruits!'), 2000)]);

try {
  // wait...
  const list = await promise;
} catch (aggregateError) {
  console.log(aggregateError); // logs AggregateError
  console.log(aggregateError.errors); 
  // logs [Error('Out of vegetables!'), Error('Out of fruits!')]
}

Try the demo.

所有输入的promise都被拒绝, Promise.any([...])返回的promise也会被拒绝,同时抛出一个特殊的错误——AggregateError ,错误中包含了所有promise被拒绝的原因。

aggregate error提供了一个特殊的属性errors:这是一个数组,包含了被拒绝的输入promise的错误。

  1. 总结

Promise.any()对于以并行和竞赛的方式执行独立的异步操作是很有用的,可以获得第一个resolved的promise的值。

如果所有输入的promise都被拒绝, Promise.any([...])返回的promise也会被拒绝,同时抛出一个特殊的错误—— aggregate error,它提供了一个属性aggregateError.errors,其中包含了所有输入promise被拒绝的原因。

注意,如果输入的列表为空, Promise.any([])也会被拒绝。

挑战:Promise.any()Promise.race()之间的主要区别是什么?