理解并实现你自己的Promise

本文主要探讨 1.promise 解决什么问题 .
                       2.promise解决的痛点,还能有哪些解决方法 。
                       3.promise 如何使用.
                      4.手动实现一个自己的promise 。
一.  Promise 出现以前,我们处理 异步网络请求,大概是这样,这还只是三个请求的样子,对于大型的项目,可能需要连续发多次请求, 这样的代码不利于阅读,更不利于维护。能不能用一种更加友好的代码组织方式,解决异步嵌套的问题,Promise出现。

getData( params,a=>{
     getData(a,b=>{                 
          getData(b,c=>{
           console.log(c);
        })
     })
})

Promise 是异步编程的一种解决方案,比传统的异步解决方案【回调函数】和【事件】更合理、更强大,已经被纳入ES6 的规范。使用promise 的 代码现在是这样的;相比回调来说,更规范易于维护,而且可以捕获异常信息,终止流程。

new Promise((resolve,reject)=>{
        setTimeout(()=>resolve('ok'))
          }).then()
          .then()
          .then()
          .catch(处理异常)

在promise 的基础上,结合Generator 、co 模块 ,现在已经衍生出了async /await 规范, 但是万变不离其宗,一起探究下promise如何实现的。上面的promise 包括 内部的回调函数(执行器),回调函数内部包括两个 参数 resolve 、reject  负责改变promise的状态,then中的函数在状态改变后执行。注意:更准确的说是 不是then函数在状态改变后执行, 而是 then中的回调函数在 promise 的 状态改变后执行。then 方法会把其中的 回调注册到对应的 resolveCallBack 和rejectCallBack队列中,等状态改变后依次执行对应队列中的回调。

要实现一个promise , 要包括那几部分?
1.执行器函数 handler ,调用promise后会立即执行,也是的入口。
2.状态 fulfiled、rejected、pending ,根据状态来执行then 中的回调。
3. 值 value , promise 的值 ,作为then 中回调函数的实参传入。
4. resolve ,reject ,执行器中传入的函数,改变promise的状态,值 。
5.then函数,挂载在构造函数的原型上,接收onFulfilled, onRejected 两个函数,分别在promise的状态变为fulfiled ,rejected时执行。

 const PENDING ='pending';
 const FULFILED ='fulfiled';
 const REJECTED = 'rejected'; //状态声明为常量

function MyPromise (handler){
       const that = this;  // 这里绑定this指针
      that.status=PENDING;
      that.value = undefined;
      that.successCallback=[];
      that.errorCallback = [];
     function resolve (successValue){};
    function reject(errorValue){ }
   try {
       handler(resolve,reject);
     }catch(e){
     reject(e)  
     }
} 
MyPromise.prototype.then=function(){};

大体的框架如上, 下面实现下各个细节部分;
1).handler  执行器函数,作为promise 的入口,将传入的回调函数执行。达到改变promise状态的目的。

2).resolve ,reject函数, promise状态只能由pending  改变为fulfiled 或者 rejected ,且只能改变一次,所以先判断是否为pending,改变状态,接收promise的value 值,依次执行then 函数注册的回调。
改变状态、赋值value,最重要的一点:循环执行then方法注册到队列中的回调。
规范中,即使是一个已经变成resolve的promise,传递给then 函数的回调也要异步方式执行。保证回调函数执行前,所有的then 函数都注册到执行队列。这里我们用setTimeout 模拟异步。 
例如这种情形, const promise=new MyPromise(resolve=>resolve('success'))
promise.then(res=>console.log('a'));
promise.then(res=>console.log('b'));
注意: 在浏览器实际的执行中,then函数会随promise的调用立即执行 ,then 函数中的回调会在promise状态变成fulfiled或rejected时候,注册到微任务队列在下一轮事件循环时候执行。如下是resolve 的实现,reject类似。

 function resolve (successValue){
       if (that.status!==PENDING)return ;
         that.status=FULFILED;
         that.value=successValue;
setTimeout(()=>that.successCallback.forEach(fun=>fun(that.value)))};
3).then函数 ,promise 原型上挂载then方法用来访问当前或者最终的值;then 中传入可选函数 onFulfiled ,onRejected  。
onFulfiled ,onRejected都不是函数的时候, 默认赋值函数,返回then所属的promise 的值. 这样是为了then 未传函数的时候,可以把promise的值传递下去,例如情形:promise.resolve('success').then().then(res=>console.log(res));
onFulfiled,必须在promise 的状态为fulfiled时候才能调用,传入参数为promise的值,只能调用一次。
onRejected,必须在promise 的状态为rejected时候才能调用,传入参数为promise的值,只能调用一次。
如上述2)中then可能多次调用,then中需要维护两个队列 , 队列中的元素是then方法内注册的回调函数(onFulfilled, onRejected),每调用一次then,就向队列中注册一个回调,它们会在promise状态改变时被依次执行。
then 中返回新的promise  便于then的链式调用。then函数新返回的promise(promise2)的状态取决于onFulfilled,onRejected以及原promise1的状态。具体表现:
onFulfilled或onRejected 函数存在, 返回的值为 res ,promise2 的状态为fulfiled ,且value值 为res.
onFulfilled或onRejected 函数在运行时,抛出错误对象error,promise2 的状态为rejected ,值为抛出的错误对象error.
promise1 的状态为 fulfiled,且onFulfilled 函数不存在, promise2 的状态为fulfiled,值为promise1 的值;
promise1 的状态为 rejected,且onRejected 函数不存在, promise2 的状态为rejected,值为promise1 的值;
基于以上说明, 我们可以补充下模块的细节如下:
 

const PENDING ='pending';
const FULFILED ='fulfiled';
const REJECTED ='rejected'; // 状态常量
function MyPromise (handler){
       const that = this;
      that.status=PENDING;
      that.value = undefined;
      that.successCallback=[];
      that.errorCallback = [];
     function resolve (successValue){
       if (that.status!==PENDING)return ;
         that.status=FULFILED;
         that.value=successValue;
      setTimeout(()=>that.successCallback.forEach(fun=>fun(successValue)),0); // 模拟then 中的回调函数在状态改变后异步执行。
        };

     function reject(errorValue){
          if (that.status!==PENDING)return ;
               that.status=REJECTED;
               that.value=errorValue;  
          setTimeout(()=>that.errorCallback.forEach(fun=>fun(errorValue)),0); // 同上
         }
     try {
       handler(resolve,reject); // 执行器函数
     }catch(e){
     reject(e) // catch 到错误对象reject
     }   
}

MyPromise.prototype.then=function(onFulfiled,onRejected){
       const self = this;
      return new MyPromise((resolveNext,rejectNext)=>{
       const  resolveNewPromise = value => {
        try{
            if( typeof onFulfiled !=='function'){
                  resolveNext(value) 
              }else {
            const res=onFulfiled(value);
            res instanceof MyPromise ? 
 res.then(resolveNext,rejectNext):resolveNext(res);  // then中的回调函数返回出来就是本身就是promise 实例,以实际返回的为主,接then函数处理,如果是是常值,将其作为promise的值resolve
        }
        }catch(e){
          rejectNext(e)
       }
    };
      const  rejectedNewPromise = value => {
          try{
      if( typeof onRejected!=='function'){
                rejectNext(value)
           }else {
            const res=onRejected(value);
          res instanceof MyPromise ? res.then(resolveNext,rejectNext):resolveNext(res);
          }
       }catch(e){rejectNext(e)}
    };
       if (self.status===PENDING){
         self.successCallback.push(resolveNewPromise);
         self.errorCallback.push(rejectedNewPromise);
        }
       if (self.status===FULFILED){
             setTimeout(()=> resolveNewPromise(self.value),0);// 当状态改变时候,将then中的回调函数注册成异步执行,在浏览器中注册成微任务在事件循环中下一轮执行。
           }
       if (self.status===REJECTED){
             setTimeout( ()=>rejectedNewPromise(self.value),0); // 同状态改变成FULFILED
       }
    });         
};


MyPromise.prototype.catch = function(onRejected){
  this.then(null,onRejected);
}
发布了16 篇原创文章 · 获赞 3 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_42317878/article/details/104633273
今日推荐