zoukankan      html  css  js  c++  java
  • Promise原理剖析

    传统的异步回调编程最大的缺陷是:回调地狱,由于业务逻辑非常复杂,代码串行请求好几层;并行请求以前也要通过引用step、async库实现。现在ES6推出了Promise,通过Promise的链式调用可以解决回调地狱问题,通过Promise.all方法可以解决并行请求的问题。现在我们通过手写Promise来彻底理解Promise的原理。
     一、构造简单Promise函数,实现Promise的then方法
    先来看一下简单的使用方法:
    var promise=new Promise(function(resolved,rejected){
      console.log(1);
      resolved('123');
    })
    promise.then(function(data){
      console.log('success'+data)
    },function(err){
      console.log('fail'+err)
    })
    

      

    注意点:
    1. new Promise传入的executor方法代码是同步执行的;
    2. promise对象的状态有三种:pending(等待态),resolved(成功态),rejected(失败态),只能从等待态转化成成功态或失败态;
    3. executor中执行resolve()方法,表示将转化为成功态,promise.then调用时执行成功的方法,executor中执行reject()方法,表示将转化为成功态,promise.then调用时执行失败的方法。
    思路:
    1. 首先构造一个类Promise,传入一个参数executor方法;
    2. 用status记录Promise状态,默认是pending,成功态是resolved,失败态是rejected, execuotor执行时,重写resolve、reject方法,执行到这两个方法时,将promise状态修改,并将参数存储起来,以便then方法调用;
    3.当实例执行then方法时,依据promise状态来执行成功方法或失败方法。
    这样就实现了这个简单的peomise,代码如下:
    function Promise(executor){
      let self=this;
      self.status='pending';
      self.value=undefined;
      self.reason=undefined;
    function resolve(value){
      if(self.status==='pending'){
        self.value=value;
        self.status='resolved';
      }
    }
    function reject(reason){
      if(self.status==='pending'){
        self.reason=reason;
        self.status='rejected';
      }
    }
      try{
        executor(resolve,reject) //第一步:先执行executor,根据里面resolve和reject的调用,执行上面的resolve和reject方法
      }catch(e){
        reject(e);
      }
    }
    Promise.prototype.then=function(onFulfilled,onRejected){
      let self=this;//第二步:then被调用的时候根据已经记录的promise状态,执行成功方法或失败方法
      if(self.status==='resolved'){
        onFulfilled(self.value);
      }
      if(self.status==='rejected'){
        onRejected(self.reason);
      }
    }
    module.exports = Promise
    二、实现Promise executor中的异步调用
    先来看一下简单使用小例子:
    var promise=new Promise(function(resolve,reject){
        console.log('hello')
        setTimeout(function(){
            resolve('ok')
        },1000);
    })
    promise.then(function(data){
        console.log('success'+data)
    },function(err){
        console.log('fail'+err)
    })        
    上面例子,先打印出hello,过1秒后执行resolve方法,打印出successok。
    注意点:
    1. executor执行一秒之后再调用resolve方法,才会执行then中的成功方法;
    思路:
    1. executor中方法没调用resolve之前,Promise方法一直是pending状态,这时候执行器已经执行完了then方法,这时我们把then方法中的成功方法和失败方法存入数组,当resolve方法调用时,去执行这些存储起来的方法;
    2. 没有异步调用的话,executor执行时执行resolve方法,成功数组 为空,所以不会被影响,还是在then方法中去执行成功或失败方法。
     
    function Promise(executor){
      let self=this;
      self.status='pending';
      self.value=undefined;
      self.reason=undefined;
      self.onResolvedCallbacks=[];
      self.onRejectedCallbacks=[];
      function resolve(value){
        if(self.status==='pending'){
          self.value=value;
          self.status='resolved';
          self.onResolvedCallbacks.forEach(item=>item());//最终执行resolve方法时,修改状态,将成功数组中的方法取出,一一执行。
        }
      }
      function reject(reason){
        if(self.status==='pending'){
          self.reason=reason;
          self.status='rejected';
          self.onRejectedCallbacks.forEach(item=>item());//最终执行reject方法时,修改状态,将失败数组中的方法取出,一一执行。
        }
      }
      try{
        executor(resolve,reject)
      }catch(e){
        reject(e);
      }
    }
    Promise.prototype.then=function(onFulfilled,onRejected){
      let self=this;
      if(self.status==='resolved'){
        onFulfilled(self.value);
      }
      if(self.status==='rejected'){
        onRejected(self.reason);
      }
      if(self.status==='pending'){ //执行then的时候是等待态,就将成功方法存入onResolvedCallbacks数组,将失败方法存入onRejectedCallbacks数组
        self.onResolvedCallbacks.push(function(){
          onFulfilled(self.value);
        });
        self.onRejectedCallbacks.push(function(){
          onRejected(self.reason);
        });
      }
    }
    module.exports = Promise
    三、实现Promise then的链式调用
    链式调用是Promise中的核心方法,也是最重要的一个方法,先来看一下链式调用的用法:
    let p=new Promise((resolve,reject)=>{
      console.log(1);
      setTimeout(function(){
        resolve('123');
      },1000);
    })
    let p2=p.then((data)=>{
      console.log(data);
      return new Promise((resolve,reject)=>{
        setTimeout(function(){
          resolve('456');
        },1000);
      });
    },err=>{
      console.log(err);
      throw Error('失败');
    }).then((data)=>{
      console.log('aaa'+data);
    },(err)=>{
      console.log(err);
    })
    上面代码首先打印出executor中的1,1秒之后执行resolve方法执行第一个then中的成功方法,先打印出123,再执行此方法中return的Promise,一秒之后执行下一个then的成功方法,打印出aaa456。
    注意点:
    1. 如果then方法返回一个普通值,执行下一个then的成功方法;
    2. 如果抛出一个错误,执行下一个then的失败方法;
    3. then方法中返回一个Promise对象,等待Promise对象执行成功就调用下个then的成功方法,这个Promise调用reject()就执行下一个then的失败方法;
    思路:
    1.在then方法后之所以可以接着调用then方法,肯定then方法需要返回一个Promise实例;
    2. 在then方法中我们用x去接收成功或失败方法的返回值,当成功或失败方法抛出错误,直接执行reject,调用下一个then的失败方法,专门写了一个方法resolvePromise分析返回值;
    3. resolvePromise方法中传入了四个参数,then中的Promise2,x值;
    4. 如果x与promise相等,报类型错误循环引用;
    5. 当x不是null,是个对象类型或者是个函数类型,并且他的then方法也是函数类型时,我们认为x是一个promise实例,执行x.then()方法;再用resolvePromise方法分析他的成功的返回值;
    6. 如果x不是null不是object或function,我们认为x是一个普通值,执行promise2中的resolve方法;
    7. 如果x.then方法获取失败,调用promise2的reject方法;
    8. 定义一个called为true,当执行过resolve或reject方法后将其变为false,保证resolve和reject方法只执行一次;
    9. 这里面存在两个递归调用,一个是then方法里面return new Promise方法,一个是resolvePromise方法,第一个的递归调用解决了executor中异步调用的问题,第二个递归调用解决了return Promise实例的问题。



    代码如下:
     
    function Promise(executor){
      let self=this;
      self.status='pending';
      self.value=undefined;
      self.reason=undefined;
      self.onResolvedCallbacks = []; // 存放then成功的回调
      self.onRejectedCallbacks = []; // 存放then失败的回调
     
      function resolve(value){
        if(self.status==='pending'){ //这里面的self不能用this替代,因为是回调执行的时候不是在类里面所以这里的this不是类函数
          self.status='resolve';
          self.value=value;
          self.onResolvedCallbacks.forEach(item=>{item()})
        }
      }
      function reject(reason){
        if(self.status==='pending'){
          self.status='reject';
          self.reason=reason;
          self.onRejectedCallbacks.forEach(item=>{item()})
        }
      }
      try{
        executor(resolve,reject);
      }catch(e){
        reject(e);
      }
    }
    function resolvePromise(promise2,x,resolve,reject){
      if(promise2===x){//自己等于自己,报类型错误
        return reject(new TypeError('循环引用了'));
      }
      let called;//控制成功或失败只能调用一次
      if(x!==null && (typeof x==='object'||typeof x==='function')){
        try{
          let then=x.then;
          if(typeof then==='function'){
            then.call(x,function(y){ //y可能还是一个promise,递归解析,直到返回普通值
              if(called)return;
              called=true;
              resolvePromise(promise2,y,resolve,reject)
            },function(err){
              if(called)return;
              called=true;
              reject(err);
            });
          }else{
            resolve(x);
          }
        } catch (e) {
          if (called) return
          called = true;
          reject(e);
        }
      } else { // 说明是一个普通值
        resolve(x);
      }
    }
    Promise.prototype.then=function(onFufilled,onReject){
      onFufilled=typeof onFufilled==='function'?onFufilled:function(value){
        return value;
      }
      onReject=typeof onReject==='function'?onReject:function(err){
        throw err;//抛出错误,才会走下个then的失败
      }
      let self=this;
      let promise2;
      if(self.status=='pending'){
        promise2=new Promise(function(resolve,reject){
          self.onResolvedCallbacks.push(function(){
            setTimeout(function(){
              try{
                let x=onFufilled(self.value);
                resolvePromise(promise2,x,resolve,reject);
              }catch(e){
                reject(e)
              }
            })
          });
          self.onRejectedCallbacks.push(function(){
            setTimeout(function(){
              try{
                let x=onReject(self.reason);
                resolvePromise(promise2,x,resolve,reject);
              }catch(e){
                reject(e);
              }
            })
          });
        })
      }
      if(self.status=='resolve'){
        promise2=new Promise(function(resolve,reject){
        setTimeout(function(){
          try{
            let x=onFufilled(self.value);
            resolvePromise(promise2,x,resolve,reject);
          }catch(e){
            reject(e)
          }
        })
      })
    }
      if(self.status=='reject'){
        promise2=new Promise(function(resolve,reject){
          setTimeout(function(){
            try{
              let x=onReject(self.reason);//这里的x可能是一个普通值,也可能是一个promise,所以写个方法统一处理。
              resolvePromise(promise2,x,resolve,reject);
            }catch(e){
              reject(e);
            }
          })
        })
      }
      return promise2;
    };
    四、检测一下我们的Promise是否符合Promise A+规范
    我们需要先给Promise类增加一个defer方法,其实就是一个语法糖。
    Promise.defer = Promise.deferred = function () {
        let dfd = {};
        dfd.promise = new Promise(function (resolve, reject) {
            dfd.resolve = resolve;
            dfd.reject = reject;
        });
        return dfd
    }            
     
    没有上面的语法糖,无法被检测。
    下载一个插件npm install -g promises-aplus-tests
    在命令行工具中执行命令:npm install -g promises-aplus-tests ./promise.js
    即可检验自己的promise写的如何。
    五、附赠Promise的all、race、resolve、reject、catch方法
     
    Promise.all=function(promises){
    //promises是一个promise的数组,同时需要返回一个promise
      return new Promise(function(resolve,reject){
        let arr=[]; //arr是最终返回值的结果集
        let i=0;
        function processData(index,y){
          arr[index]=y;
          if(++i===promises.length){
            resolve(arr);
          }
        }
        for(let i=0;i<promises.length;i++){ //因为then的时候,for循环已经走了,i值就是最大的,所以这里用let,保证每次i都是都是当前值
          promises[i].then(function(y){
            processData(i,y);
          },function(err){
            reject();
          })
        }
      })
    }
    Promise.race=function(promises){
      return new Promise(function(resolve,reject){
        for(let i=0;i<promises.length;i++){
          promises[i].then(function(data){
            resolve(data);
          },function(err){
            reject(err);
          })
        }
      })
    }
    Promise.resolve=function(value){
      return new Promise(function(resolve,reject){
        resolve(value);
      })
    }
    Promise.reject=function(reason){
      return new Promise(function(resolve,reject){
        reject(reason);
      })
    }
     
    Promise.prototype.catch = function (callback) {
      return this.then(null, callback)
    }
  • 相关阅读:
    《DSP using MATLAB》 示例 Example 9.12
    《DSP using MATLAB》示例 Example 9.11
    《DSP using MATLAB》示例 Example 9.10
    《DSP using MATLAB》示例Example 9.9
    《DSP using MATLAB》示例 Example 9.8
    《DSP using MATLAB》示例Example 9.7
    《DSP using MATLAB》示例 Example 9.6
    《DSP using MATLAB》示例Example 9.5
    《DSP using MATLAB》示例 Example 9.4
    (转载)【C++11新特性】 nullptr关键字
  • 原文地址:https://www.cnblogs.com/learnings/p/9392481.html
Copyright © 2011-2022 走看看