You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
// 定义Promise的三种状态常量constPENDING='PENDING';constFULFILLED='FULFILLED';constREJECTED='REJECTED';functionPromise(resolver){// 传入的必须是函数if(typeofresolver!=='function'){thrownewTypeError('Promise resolver '+resolver+' is not a function');}// resolve或reject的结果值this._result=undefined;// 状态this._status=PENDING;try{// 执行resolver(this._resolve.bind(this),this._reject.bind(this));}catch(error){// 捕获错误this._reject(error);}}// 私有方法,传给resolver的成功、失败回调Promise.prototype._resolve=function(){}Promise.prototype._reject=function(){}
letpromise=undefined;return(promise=newPromise((resolve,reject)=>{setTimeout(()=>{try{varx=onFulfilled(this._result);// 或者 var x = onRejected(this._result);// resolvePromise Promise解决过程 下一段讲resolvePromise(promise,x,resolve,reject);}catch(e){returnreject(e);}});}));
functionresolvePromise(promise,x,resolve,reject){// 如果 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promiseif(x===promise){returnreject(newTypeError('Chaining cycle detected for promise!'));}// 用于 “优先采用首次调用并忽略剩下的调用”的标志位letinvoked=false;// 尝试把 x.then 赋值给 thenletthen=undefined;// x 为对象或函数if((x!==null&&typeofx==='object')||typeofx==='function'){try{then=x.then;if(typeofthen==='function'){// 如果 then 是函数,将 x 作为函数的作用域 this 调用之then.call(x,(y)=>{if(invoked)return;invoked=true;returnresolvePromise(promise,y,resolve,reject);},(r)=>{if(invoked)return;invoked=true;returnreject(r);});}else{// 如果 then 不是函数,以 x 为参数执行 promisereturnresolve(x);}}catch(e){// 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promiseif(invoked)return;invoked=true;returnreject(e);}}else{// 如果 x 不为对象或者函数,以 x 为参数执行 promisereturnresolve(x);}}
Promise 是异步编程的一种解决方案,比传统的解决方案(回调函数和事件)更合理和更强大。现在前端应用中Promise已经得到了广泛使用。本文通过实现符合Promise/A+规范的Promise,对其加深印象。
构造函数
我们在使用Promise时,通常是使用new操作符进行构造,传入resolver函数,该函数会接受成功(resolve)、失败(reject)的回调函数,当我们确定结果时,需要调用resolve或reject,具体代码如下:
所以我们的Promise也需要是个构造函数,并且执行用户传入的resolver函数,将定义好的回调函数传进去。下面是具体的代码:
注:本文代码的实现,下划线开头代表私有属性、私有方法。
接下来我们来实现_resolve、_reject私有方法,其实逻辑很简单,我们只需要改变Promise状态,以及成功的值或者失败的原因。但要注意Promise一旦状态改变,就不会再变,任何时候都可以得到这个结果,所以我们只有在状态时PENDING的时才会执行。
Promise.prototype.then
Promise的核心就是
then
方法,Promise/A+规范大都也是针对then方法进行阐述,实现了then
方法后,我们再来实现其他API就方便了很多。Promise的
then
方法的规范有如下几点接受两个参数,onFulfilled(成功回调), onRejected(失败回调),当回调不是函数时, 其必须被忽略,支持透传
then 方法可以被同一个 Promise 调用多次
then 方法必须返回一个 Promise 对象
我们针对上述几点分别来实现一下
关于第一点,可以控制台执行下面代码
第二个then方法依然可以接受到resolve成功的值,所以当then方法传入的不是函数时,我们要规范使其变成函数支持透传。
实现第一点很简单,我们只需要吧把结果/错误 -> 返回/抛出传递给出去就可以啦。
我们再来看第二点,为了可以多次调用并且依次执行,我们需要改下之前写过的代码,我们需要增加俩个回调队列,成功、失败各一个。其实也可以用一个队列来存储,我这里采用的分别存储。
我们在then方法中,如果状态还处于PENDING,就需要将传入的onFulfilled(成功回调), onRejected(失败回调)插入对应的队列中,否则直接执行就好。这也就是我们要实现第三点的核心逻辑。
根据第三点所述,我们总是需要执行onFulfilled 或 onRejected,然后传入Promise解决过程,此外还需要捕获这个过程,直接reject。具体核心代码如下
下面我们就是把这段代码分别用在 PENDING, FULFILLED, REJECTED三种状态,完整代码如下:
上面代码看似很多,很复杂,但其实根据规范来看,其实很简单,而且有大量的重复代码。那我们还有一个
resolvePromise
函数没有完成,接下来我希望读者可以自己去读一下Promise 解决过程的逻辑。点击链接去查看。因为函数的实现完全照规范的逻辑书写,没有技巧可言。这里简单的总结几点:
为了和其他promise并存,我们不能只判断onFulfilled,onRejected函数返回的是否是promise,我们只需要保证其返回值存在
then
方法就去尝试按promise处理。如果没有then属性,或者then属性不是函数的话,直接按照resolve(x)处理
如果then存在并且是函数,按照promise处理的同时,需要捕获错误按reject(x)处理
如果传入的回调均被调用,或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用(只能调用一次,需要有标志位)
按照上述四点,我们可以写出
resolvePromise
函数的代码Promise.prototype.catch
.catch()发生错误时的回调函数 相当于使用.then(null, onRejected),我们实现了then方法,所以catch方法就相当简单
Promise.prototype.finally
.finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。
上面代码中,不管promise最后的状态,在执行完then或catch指定的回调函数以后,都会执行finally方法指定的回调函数。
那我们就可以通过then方法,传入的成功、失败回调函数中都去执行callback
注意我们要.then将结果透传,因为finally后面还可以继续调用then方法。
Promise.resolve
有时需要将现有对象转为 Promise 对象,Promise.resolve()方法就起到这个作用。
我们只需要把then方法中的成功逻辑拿出来使用就可以。(其中x不是onFulfilled执行的值,直接是传入的参数)
Promise.reject
Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected。
Promise.all
Promise.race
总结
Promise的实现难点主要集中在then方法上,其他方法都是基于then方法实现的。实现一个Promise也是笔试的高频题目,希望本文章可以给你带来帮助。
完整源码传送门
参考
Promise/A+规范中文版
阮一峰ES6
手写Promise最简20行版本,实现异步链式调用
剖析Promise内部结构,一步一步实现一个完整的、能通过所有Test case的Promise类 xieranmaya/blog#3
Promise实现原理(附源码)掘进
The text was updated successfully, but these errors were encountered: