ECMAScript 提案:Promise.any()

由 Mathias Bynens, Kevin Gibbons 和 Sergey Rubanov 提出的 ECMAScript 提案 "promise.any()" 为 JavaScript 提出一个新的 Promise 方法。这边博客文章解释它如何工作。

#背景知识

下面有两个有利于理解这篇文章的背景知识资源:

#Promise.any()

promise.any() 的类型签名是:

Promise.any<T>(promises: Iterable<Promise<T>>): Promise<T>

Promise.any() 返回一个 Promise p。它的结果取决于参数 promises(迭代 Promises 的结果引用):

  • 如果有一个 Promise 的状态为成功(fulfilled),p 的状态为成功(resolved)。
  • 如果所有的 Promises 都失败,p 的状态将失败(rejected),结果值是包含所有失败状态的 AggregateError 的实例的结果集。

AggregateError 的类型签名是(删除了一些成员):

class AggregateError {
  constructor(errors: Iterable<any>, message: string);
  get errors(): Array<any>;
  get message(): string;
}

下面的插图说明了 Promise.any() 如何工作:

Promise.any 的说明插图

现在展示一个 Promise 成功时发生了什么:

const promises = [
  Promise.reject('ERROR A'),
  Promise.reject('ERROR B'),
  Promise.resolve('result'),
];
Promise.any(promises)
  .then((result) => assert.equal(
    result, 'result'
  ));

现在展示一个 Promise 失败时发生了什么:

const promises = [
  Promise.reject('ERROR A'),
  Promise.reject('ERROR B'),
  Promise.reject('ERROR C'),
];
Promise.any(promises)
  .catch((aggregateError) => assert.deepEqual(
    aggregateError.errors,
    ['ERROR A', 'ERROR B', 'ERROR C']
  ));

#Promise.any() 与 Promise.all() 对比

有两个方法对比 Promise.any()Promise.all()

  • 它们彼此相反:
    • Promise.all(): 第一个输入拒绝将拒绝结果 Promise 或它的实现值是一个具有输入实现值的数组。
    • Promise.any(): 第一个输入实现满足结果 Promise 或它的拒绝值是一个带有输入拒绝值的数组。
  • 有不同的重点:
    • Promise.all() 对所有成功感兴趣。相反的情况(至少一个拒绝)导致拒绝。
    • Promise.any() 对第一个实现很感兴趣。相反的情况(仅拒绝)导致拒绝。

#Promise.any() 与 Promise.race() 对比

Promise.any()Promise.race() 也可以联系起来,但有趣的是它们的不同:

  • Promise.race() 对完成感兴趣。首先完成的 Promise 是“胜利”。换句话说:我们想知道首先完成的异步计算。
  • Promise.any() 对成功感兴趣。首先成功的 Promise 是“胜利”。换句话说:我们想知道首先成功的异步计算。

主要(相对罕见)的用例 .race()超时 Promises.any() 的用例范围更广。接下来,我们来看它们。

#Promise.any() 的用例

如果我们有多个异步计算,同时仅仅对第一个成功的感兴趣,就可以使用 Promise.any()。在某种程度上,我们让计算彼此竞争,并使用最快的一个

以下代码演示了下载资源时的展示:

const resource = await Promise.any([
  fetch('https://example.com/first.txt')
    .then((response) => response.text()),
  fetch('https://example.com/second.txt')
    .then((response) => response.text()),
]);

我们可以使用相同的模式来使用更快下载的模块:

const lodash = await Promise.any([
  import('https://primary.example.com/lodash'),
  import('https://secondary.example.com/lodash'),
]);

作为比较,下面的代码展示我们在主服务器出错时使用备份的第二服务器:

let lodash;
try {
  lodash = await import('https://primary.example.com/lodash');
} catch {
  lodash = await import('https://secondary.example.com/lodash');
}

#如何实施 Promise.any()?

简单的实现 Promise.any() 基本上是的类似 Promise.all() 的镜像版本。在 Promise 的博客文章中查看代码,以获取更多信息。

#使用 Promise.any()