源码篇(八):手写promise版mini源码。附送简版promise源码

2,287 阅读6分钟

前言

promise是前端同步代码执行机制的一个重点手段。也许你今天用的已经是es7的await,但理解好什么是promise依然是前端的重点。我们逐步分析一下promise的实现。

再同步一下笔者的博客源码系列的进度:

| 序号 | 博客主题 | 相关链接 | |-----|------|------------|- | 1 | 手写vue_mini源码解析 | juejin.cn/post/684790… | | 2 | 手写react_mini源码解析 | juejin.cn/post/685457… | | 3 | 手写webpack_mini源码解析 | juejin.cn/post/685457… | | 4 | 手写jquery_mini源码解析| juejin.cn/post/685457… | | 5 | 手写vuex_mini源码解析(即本文) | juejin.cn/post/685529… | | 6 | 手写vue_route源码解析 | juejin.cn/post/685956… | | 7 | 手写diff算法源码解析 | juejin.cn/post/686881… | | 8 | 手写promise源码解析 | juejin.cn/post/686920… | | 9 | 手写原生js源码解析(手动实现常见api) | 预计9月 | | 10 | 手写react_redux,fiberd源码解析等 | 预计9月| | 11 | 手写koa2_mini | 预计9月,前端优先 |

promise的概念

在传统的项目中,前端解决异步问题,只能用无止境的callback回调。其实小的项目,或者简单点的逻辑,callback无疑是最高效的。然后项目逐步扩大时,就会出现,所谓的回调地狱。可能我们无法定位到入口在哪里,可能也会出现调用重复的问题等等,且问题十分不易跟踪,这时候社区(或者ES6规范)就引入了几个异步的解决方案:

  • 回调函数
  • 事件监听
  • 发布/订阅
  • Promise对象
  • await

其中,Promise 是设计用于改善 JS 中的异步编程,与事件及回调函数对比,在异步操作方面为你提供了更多的控制权与组合性。 Promise 调度被添加到 JS 引擎作业队列,以便稍后执行。不过此处有另一个作业队列追踪着 Promise 的完成与拒绝处理函数,以确保适当的执行。

当然,promise也有一些缺陷:

  • 1)无法取消Promise,一旦新建它就会立即执行,无法中途取消。
  • 2)如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。
  • 3)当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
  • 4)then的写法相比await,明显在程序代码抒写上,更加繁琐。

我们下述,手写一下promise分析一下整个过程。

手写promise/A+的过程

Promise/A+并未规范race、all、catch方法,这些是ES6自己规范的。本文重点在Promise本身,故重点在于Promise自身的构建,以及then函数的实现。

构造函数

我们先分析一下构造函数需要处理什么?

  • 1.Promise有是三个状态,分别为pedding, rejected, refulled。默认为pedding。 首先我们要记录这个状态,_status。
  • 2.每一个Promise,都有自己的resolve跟reject方法,我们定义asynFulliled,asynRejected 如果状态转换为rejected时候,调用reject。 如果状态转换为refulled时候,调用resolve
  • 3.执行完当前的Promise,如果程序正常,将进入下一步的resolve跟reject方法,即nextResolve跟nextReject。
  • 4.调用下一个Promise时,当前Promise的值,将传为下一个的Promise。我们定义_param来存储他。

根据分析,我们可以简单列出下述代码:

class ZPromise{
    constructor(){
        var self = this;
        self._param = null;//返回值
        self._status = PEDDING;//添加状态
        //then方法返回的promise对象的resovle和reject
        self.nextResolve = null;//
        self.nextReject = null;//
        //记录then的方法参数
        self.asynFulliled = null;
        self.asynRejected = null;

        self.then = function( onFulliled, onReject ){
           //
        }
    }
  }

首次执行handle方法

我们再来看看,如何新建一个promise或者then方法的:

  new ZPromise(function (resolve, reject) {
      ...         
  }).then( function(result){
      ...
  })

构造首次执行后,首先需要执行第一个...的东西。我们把他定义为handle。此时,第一个Promise可能会执行resolve()或者reject()。那么这个resolve或者或者reject是个什么东西么?我们来写一个_resolve与_reject实现他。

  • 首先resolve(或reject)是不是会改变_status状态。例如resolve之后,promise的状态,将变化为rejected。
  • 其次,下一个promise传入的参数,即使上一个promise的resolve的值,我们用_param来储存他。
  • 如果此时有下一个promise,将继续调用下一个promise的方法(下述then的讲述,会将下一个promise的方法告诉该函数)

修改一下构造函数:

class ZPromise{
    constructor(handle){
    	...
        handle(  _resolve,  _reject );
    }
}

function _resolve( val ){
    self._status = REFULLED;
    self._param = val;
 }
 
 function _reject( error ){
      _status = REJECTED;
      _param = error;
}

then方法

我们再分析then方法后处理什么?

  • 首先,then方法也是一个promise,如果不是,我们需要帮他构造成promise。即指向self对象的constructor原型。

  • then方法,需要根据对象的状态:

  • 如果是上一个promise是失败状态,这调用失败的回调(我们叫它:doAsynRejected);

  • 如果是上一个promise是成功状态,这调用成功的回调(我们叫它:doAsynFulliled);

  • 如果还在执行中,我们将当前的对象重新赋值于nextResolve,nextReject,asynFulliled,asynRejected

    self.then = function( onFulliled, onReject ){
           return new self.constructor( function( resovle, reject ){
               if(  self._status == REFULLED ){
                   doAsynFulliled( onFulliled,  resovle, reject );
               } else if ( self._status == REFULLED ){
                   doAsynRejected( onReject,  resovle, reject );
               } else {//即pedding的时候 往上执行
                   self.nextResolve = resovle;
                   self.nextReject = reject;
                   self.asynFulliled = onFulliled;
                   self.asynRejected = onReject;
               }
           })
       }
    

在handle首次初始化之前,此时的状态肯定是pedding, 为了方便下一个promise(即是then中的那个promise)知道对应的,resovle跟reject,还有对应的promise, 我们分别储存起来(promise分别对应的成功与失败)。

假设成功,那么我们即构建下一个promise,此时调用doAsynFulliled。

  • 此时下一个promise依然是个构造函数(即function),我们则重新建立一个promise, 否则说明已经结束,resolve返回最后的值即可。

  • 此时如果下一个promise的then中含有另外一个promise,则需要等他那个promise全部执行完毕。那我们需要等内部的promise执行完毕。

    function doAsynFulliled( onFulliled, resolve, reject ){ if( typeof onFulliled == 'function' ){ let promise = onFulliled( self._param ); if( promise == undefined ){//说明首次 resolve( self._param ); } else if ( promise.constructor == self.constructor ){ promise.then( function(param){ resolve( param ); }, function( param ){ reject( param ); }) } else { resolve( self._param ); } } else { resolve( self._param ); } }

同理:

  function doAsynRejected(  onRejected, resolve, reject ){
          if( typeof onRejected == 'function' ){
              let promise = onFulliled( self._param );
              if( promise == undefined ){//说明首次
                  reject( self._param );
              } else if ( promise.constructor ==  self.constructor ){
                  promise.then( function(param){
                      resolve( param );
                  }, function( param ){
                      reject( param );
                  })
              } else {
                  reject( self._param );
              }
          } else {
              reject( self._param );
          }
      }

我们整理一下:

var PEDDING = "pedding";
var REJECTED = "rejected";
var REFULLED = "refulled";

class MyPromise{

    constructor(handle){

        if( typeof("handle") == "function" ){
            throw new Error("MyPromise handle is not a function");
        }
        var self = this;
        self._param = null;//返回值
        self._status = PEDDING;//添加状态
        //then方法返回的promise对象的resovle和reject
        self.nextResolve = null;//
        self.nextReject = null;//
        //记录then的方法参数
        self.asynFulliled = null;
        self.asynRejected = null;
        handle(  _resolve,  _reject );

        self.then = function( onFulliled, onReject ){
            return new self.constructor( function( resovle, reject ){
                if(  self._status == REFULLED ){
                    doAsynFulliled( onFulliled,  resovle, reject );
                } else if ( self._status == REFULLED ){
                    doAsynRejected( onReject,  resovle, reject );
                } else {//即pedding的时候 往上执行
                    self.nextResolve = resovle;
                    self.nextReject = reject;
                    self.asynFulliled = onFulliled;
                    self.asynRejected = onReject;
                }
            })
        }

        function _resolve( val ){
            self._status = REFULLED;
            self._param = val;
            if( self.nextResolve ){
                doAsynFulliled( self.asynFulliled, self.nextResolve, self.nextReject );
            }
        }

        function _reject( error ){
            _status = REJECTED;
            _param = error;
            if( nextReject ){
                doAsynRejected( asynRejected, nextResolve, nextReject );
            }
        }

        function doAsynFulliled( onFulliled, resolve, reject ){
            if( typeof onFulliled == 'function' ){
                let promise = onFulliled( self._param );
                if( promise == undefined ){//说明首次
                    resolve( self._param );
                } else if ( promise.constructor ==  self.constructor ){
                    promise.then( function(param){
                        resolve( param );
                    }, function( param ){
                        reject( param );
                    })
                } else {
                    resolve( self._param );
                }
            } else {
                resolve( self._param );
            }
        }

        function doAsynRejected(  onRejected, resolve, reject ){
            if( typeof onRejected == 'function' ){
                let promise = onFulliled( self._param );
                if( promise == undefined ){//说明首次
                    reject( self._param );
                } else if ( promise.constructor ==  self.constructor ){
                    promise.then( function(param){
                        resolve( param );
                    }, function( param ){
                        reject( param );
                    })
                } else {
                    reject( self._param );
                }
            } else {
                reject( self._param );
            }
        }


    }

}

此时,一个简单版的promise即完成。

特别感谢

看了很多个版本的promise,觉得该文的promise最好理解。即借鉴了思维,感谢他: segmentfault.com/a/119000001…

项目地址

完整代码请看git:

github.com/zhuangweizh…