手写Promise的6大静态方法
引言
大家好啊,我是前端拿破轮。
在promise相关的笔面试题目中,经常考察的一种题目就是手写Promise。
由于面试时间有限,所以让我们完整地实现Promise的可能性比较小。所以经常考察的便是手写Promise的6大静态方法。
这篇文章拿破轮就带着大家深入分析6大静态方法各自的功能并给出代码实现。
首先先来回顾一下Promise的静态方法主要包括以下几种:
Promise.resolvePromise.rejectPromise.allPromise.anyPromise.allSettledPromise.race
Promise.resolve
Promise.resolve()静态方法将给定值解析为Promise。如果值为promise,则直接返回该promise;如果值是一个thenable,则Promise.resolve()将使用它准备的两个回调调用then()方法,否则,返回的promise就将使用value。该函数将嵌套的类 Promise 对象(例如,一个将被兑现为另一个 Promise 对象的 Promise 对象)展平,转化为单个 Promise 对象,其兑现值为一个非 thenable 值。
1 | const promise1 = Promise.resolve(123); |
什么意思呢?总结一下就是3种情况。当我们使用Promise.resolve(value)时,其内部的处理取决于value的值。
- 如果
value是一个proimse,则直接返回该promise; - 如果
value是一个thenable, 则会展开该thenable,直到resovle的值不再是thenable。 - 如果
value是其他情况,则直接返回fulfilled的promise,值就是value。
所以手写代码其实非常简单
1 | Promise.myResolve = (value) => { |
你可能会在很多地方看到说上面的写法不完善,因为没有处理thenable对象,这种说法是错误的,上面的代码已经能够非常好地模拟原生的Promise.resolve()的实现方式了。因为我们在新返回的Promise的executor中调用了resolve方法,这个会自动处理thenable对象,将其展开,所以不用我们额外处理。
Promise.reject
Promise.reject()静态方法返回一个已拒绝(rejected)的Promise对象,拒绝的原因就是给定的参数。
1 | function resolved(result) { |
与Promise.resolve不同,即使reason已经是一个Promise对象,Promise.rejected()方法也始终会将其封装在一个新的Promise对象中。
1 | const p = Promise.resolve(1); |
所以要实现Promise.reject就更简单了,直接返回一个新的Promise并在executor中直接调用reject(reason)即可。手写代码如下所示:
1 | Promise.myReject = (reason) => { |
Promise.all
Promise.all()静态方法接受一个Promise可迭代对象作为输入,并返回一个Promise。当所有输入的Promise都被兑现时,返回的Promise也将被兑现(即使传入的是一个空的可迭代对象),并返回一个包含所有兑现值的数组。如果输入的任何Promise被拒绝,则返回的Promise将被拒绝,并带有第一个被拒绝的原因。
什么意思呢?我们来看一个例子:
1 | const promise1 = Promise.resolve(3); |
在上面的例子中,promise1是fulfilled状态,值为3。
promise2是一个普通数值,并非promise,所以我们由此可知,Promise.all在面对传入不是promise实例的对象时,应该会对其使用Promise.resolve包装成一个promise对象。
promise3是pending状态,100ms后执行resolve('foo'),成为fulfilled的状态,值为foo。
所以下面的Promise.all最初也是pending状态,由于有promise3是pending。100ms后,promise3变成fulfilled,所以Promise.all()也成为fulfilled,值为三个promise的值组成的数组。[3, 42, 'foo']。
所以整个代码会再100ms后输出[3, 42, 'foo']。
根据上述特性,我们不难写出以下手写实现Promise.all
1 | Promise.myAll = (promises) => { |
Promise.any
Promise.any()静态方法将以一个Promise可迭代对象作为输入,并返回一个Promise。当输入的任何一个Promise兑现时,这个返回的Promise将会兑现,并返回第一个兑现的值。当所有输入的Rromise都被拒绝(包括传递了空的可迭代对象)时,它会以一个包含拒绝原因的AggregateError拒绝。
如下示例:
1 | const promise1 = Promise.reject(0); |
简单来说,Promise.any的返回值有三种情况:
- 已拒绝:如果传入的
iterable为空的话,则返回值为已拒绝的Promise。 - 异步兑现:传入的
iterable中有任何一个Promise被兑现时,返回的Promise就会被兑现,其兑现值是第一个兑现的Promise的兑现值。 - 异步拒绝:传入的
iterable中的Promise都被拒绝时。返回的Promise也拒绝。拒绝原因是一个AggregateError,其errors属性包含一个拒绝原因的数组。无论完成顺序如何,这些错误都是按照传入的Promise的顺序排序。如果传递的iterable是非空的,但不包含待定pending的Promise,则返回的Promise仍然是异步拒绝的(而不是同步拒绝的)。
Promise.any() 会以第一个兑现的 Promise 来兑现,即使有 Promise 先被拒绝。这与 Promise.race() 不同,后者会使用第一个敲定的 Promise 来兑现或拒绝。
1 | const pErr = new Promise((resolve, reject) => { |
1 | const failure1 = new Promise((resolve, reject) => { |
根据上述分析,不难实现手写如下代码
1 | Promise.myAny = (promises) => { |
Promise.allSettled
Promise.allSettled()静态方法将一个Promise可迭代对象作为输入,并返回一个单独的Promise。当所有输入的Promise都已经敲定(包括传入空的可迭代对象时),返回的Promise将被兑现,并带有描述每个Promise结果的对象数组。
注意:
Promise.allSettled()的返回结果永远不可能是rejected
1 | const promise1 = Promise.resolve(3); |
这里要注意Promise.allSettled()返回的Promise状态对空数组的处理和Promise.any()是不同的。Promise.allSettled()面对传入的是空数组的情况下会返回已兑现fulfilled的promise。而Promise.any()则是返回已拒绝,这里一定要注意。
总结来看,Promise.allSettled()的返回值是一个Promise只会有两种状态:
- 已兑现(already fulfilled),如果传入的
iterable为空的h话 - 异步兑现(asyncChronously fulfill),当给定的
iterable中所有的promise已经敲定(settled)时。兑现值是一个对象数组,其中的对象按照iterable中传递的promise的顺序,描述每一个promise的结果,无论完成的顺序如何。每个结果对象都有以下属性:status:一个字符串,要么是fulfilled,要么是rejected,表示promise的最终状态。value:仅当status为fulfilled,才存在。promise的兑现值。reason:仅当status为rejected,才存在。promise的拒绝原因。
根据上述描述,我们不难写出如下手写代码:
1 | Promise.myAllSettled = (promises) => { |
Promise.race
Promise.race()静态方法接受一个promise可迭代对象,并返回一个Promise。这个返回的Promise的状态会随着第一个Promise的敲定而敲定。
看下面的例子
1 | const promise1 = new Promise((resolve, reject) => { |
Promise.race()的返回值,会根据iterable中第一个敲定的promise的状态异步敲定。换句话说,如果第一个敲定的 promise 被兑现,那么返回的 promise 也会被兑现;如果第一个敲定的 promise 被拒绝,那么返回的 promise 也会被拒绝。
如果传入的iterable为空,返回的promise就会一直保持待定状态。如果传入的iterable非空但其中没有任何一个 promise 是待定状态,返回的 promise 仍会异步敲定(而不是同步敲定)。
根据上述描述,容易写出以下代码
1 | Promise.myRace = (promises) => { |
总结
本文总结了Promise的6种静态方法的特性,并实现了使用js手写模拟实现。六种静态方法中Promise.resolve()和Promise.reject()是根据传入的值得到一个promise,而剩下的Promise.all(), Promise.any(),Promise.allSettled()和Promise.race()都是用来进行并发控制的静态方法,他们的参数往往是一个promise的数组。
这里要尤其注意一下对于空数组的处理
如果传入的是空数组,四个并发控制方法返回情况如下:
Promise.all():已兑现(already fulfilled),同步实现Promise.any():已拒绝(already rejected),同步实现Promise.allSettled():已兑现(already fulfilled),同步实现Promise.race():一直保持待定(pending)状态
好了,这篇文章就到这里啦,如果对您有所帮助,欢迎点赞,收藏,分享👍👍👍。您的认可是我更新的最大动力。由于笔者水平有限,难免有疏漏不足之处,欢迎各位大佬评论区指正。
往期推荐✨✨✨
我是前端拿破轮,关注我,一起学习前端知识,我们下期见!


