MENU

Analysis of Polyfills for Promise.any

2021 年 07 月 15 日 • 阅读: 4821 • 前端

本文将分析 Promise.any() 的两个 PolyfillPromise.any() 是一项 ECMAScript 提案,功能与 Promise.all() 相反。下面是 MDN 对这两个函数的介绍:

The Promise.all() method takes an iterable of promises as an input, and returns a single Promise that resolves to an array of the results of the input promises. This returned promise will resolve when all of the input's promises have resolved, or if the input iterable contains no promises. It rejects immediately upon any of the input promises rejecting or non-promises throwing an error, and will reject with this first rejection message / error.

Promise.any() takes an iterable of Promise objects and, as soon as one of the promises in the iterable fulfills, returns a single promise that resolves with the value from that promise. If no promises in the iterable fulfill (if all of the given promises are rejected), then the returned promise is rejected with an AggregateError, a new subclass of Error that groups together individual errors. Essentially, this method is the opposite of Promise.all().

看一下使用示例:

Promise.any([
  fetch("https://google.com/").then(() => "google"),
  fetch("https://apple.com").then(() => "apple"),
  fetch("https://microsoft.com").then(() => "microsoft"),
])
  .then((first) => {
    // Any of the promises was fulfilled.
    console.log(first);
  })
  .catch((error) => {
    // All of the promises were rejected.
    console.log(error);
  });

Reversing Promise.all

第一个 polyfill 利用 Promise.any()Promise.all() 功能相反的条件,通过两次反转完成操作。

// https://github.com/m0ppers/promise-any/blob/master/index.js

'use strict';

function reverse(promise) {
    return new Promise((resolve, reject) => Promise.resolve(promise).then(reject, resolve));
}

module.exports = function promiseAny(iterable) {
    return reverse(Promise.all([...iterable].map(reverse)));
};

首先,函数将 iterable 中每个 promise 的 reject 和 resolve 对调,随后将新 promise 数组传给 Promise.all()。此时,相对于原始 iterable 中的 promises,Promise.all() 会在它们全部 rejected 时 resolve,或在任意一个 promise fulfilled 时 reject。最后反转 Promise.all(),此时相对于原始 iterable 中的 promises,得到的最终 Promise 会在它们全部 rejected 时 reject,或在任意一个 promise fulfilled 时 resolve,这就是 Promise.any() 的语义。见下表比较:

const i = iterableconst r = [...iterable].map(reverse)Promise.all(r)reverse(Promise.all(r)) / Promise.any(i)
any of them fulfilledany of them rejectedrejectedfullfilled
all rejectedall fulfilledfullfilledrejected

Iterating all Promises

第二个 polyfill 不依赖 Promise.all,而是按照 Promise.any() 语义正向实现。

// https://github.com/ungap/promise-any/blob/master/index.js

var any = (Promise.any || function ($) {
  return new Promise(function (D, E, A, L) {
    A = [];
    L = $.map(function ($, i) {
      return Promise.resolve($).then(D, function (O) {
        return ((A[i] = O), --L) || E({errors: A});
      });
    }).length;
  });
}).bind(Promise);

我们主要关注几个变量,$ 代表输入的所有 promises;DresolutionFunc,它执行后,any() 就将处于 fulfilled 状态;ErejectionFunc,它执行后,any() 就将处于 rejected 状态;A 是一个数组,用于记录 rejected promise 的原因;L 是 fulfilled promise 的个数。见下表:

VariableMeaning
$ (outer)iterable of promises
DresolutionFunc
ErejectionFunc
Aarray of rejected reasons
Lnumber of fulfilled promises

那么,Promise.resolve($).then(D... 的含义就是,只要有一个 promise fullfilled,就调用 D 去 fullfill 最终 promise。...--L) || E({errors: A}) 的含义就是,如果 L 为 0,即所有 promise 都被 rejected,就调用 E 去 reject 最终 promise。这符合 Promise.any() 的语义。

References

TG 大佬群 QQ 大佬群

返回文章列表 文章二维码
本页链接的二维码
打赏二维码
添加新评论

Loading captcha...