本文将分析 Promise.any()
的两个 Polyfill
。Promise.any()
是一项 ECMAScript 提案,功能与 Promise.all()
相反。下面是 MDN 对这两个函数的介绍:
The
Promise.all()
method takes an iterable of promises as an input, and returns a singlePromise
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 ofPromise
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 anAggregateError
, a new subclass ofError
that groups together individual errors. Essentially, this method is the opposite ofPromise.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 = iterable | const r = [...iterable].map(reverse) | Promise.all(r) | reverse(Promise.all(r)) / Promise.any(i) |
---|---|---|---|
any of them fulfilled | any of them rejected | rejected | fullfilled |
all rejected | all fulfilled | fullfilled | rejected |
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;D
是 resolutionFunc
,它执行后,any()
就将处于 fulfilled
状态;E
是 rejectionFunc
,它执行后,any()
就将处于 rejected
状态;A
是一个数组,用于记录 rejected promise 的原因;L
是 fulfilled promise 的个数。见下表:
Variable | Meaning |
---|---|
$ (outer) | iterable of promises |
D | resolutionFunc |
E | rejectionFunc |
A | array of rejected reasons |
L | number 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
如有问题请在下方留言,文章转载请注明出处,详细交流请加下方群组!请大佬不要屏蔽文中广告,因为它将帮我分担服务器开支,如果能帮忙点击我将万分感谢。