• Promise原理与实现探究的一种思路


    写在前面


    这个文章,展现的是一个实现Promise的思路,以及如何发现和处理问题的情境。

    从现有的Promise分析


    如果我们想要自己实现一个简单的Promise,那现有规范规定的Promise肯定是我们最好的参照。

    我们先看下Promise怎么使用:

    var promise1 = new Promise(function(resolve, reject){
          // 成功后的TODO
          resolve(value);
          // 失败后的TODO
          reject(err);
    })
    

    来看下返回的promise1是什么,以及它的结构是怎么样的:

    image

    再进行一些具体操作

    var promise1 = new Promise(function(resolve, reject) {
     	resolve('zqz')
    })
    
    promise1.then(function(result) {
       console.log(result) 
    }).catch(function(err){
       console.log(err) 
    })
    
    // => 'zqz'
    
    11
    var promise1 = new Promise(function(resolve, reject) {
     	reject('出现异常')
    })
    promise1.then(function(result) {
       console.log(result) 
    }).catch(function(err){
       console.log(err) 
    })
    
    // => '出现异常'
    
    
    22

    从Promise的 使用方式上 和 实例 可以看到哪些东西:

    • Promise是一个构造函数
    • Promise包含一个参数,这个参数类型是一个_匿名函数_
    • 匿名函数包括2个形参,分别是 rejectresolve
    • 这两个形参类型是 函数 ,且 rejectresolve 都有一个参数, 参数类型不限定
    • 实例 是个 Promise
    • 实例的 原型 上挂载了 2个方法,分别是 thencatch,同时then可以有多个,所以需要一个回掉函数队列
    • 实例上 有2个属性,分别是 PromiseStatusPromiseValue
    • Promise根据定义 PromiseStatus 需要有 3种状态,分别是 pending , fulfilledrejected

    根据上面的分析情况,我们先简单的来构造一个雏形。

    function Promise(fn) {
      this.PromiseStatus = 'pending';
      this.PromiseValue = '';
    
      this.resolvedCb = [];
      this.rejectedCb = [];
    
      var self = this;
    
      var resolve = function (result) {
        // 判断状态
         if (self.PromiseStatus === 'pending') {
          self.PromiseStatus = 'resolved';
          self.PromiseValue = result;
           // resolvedCb 队列依次执行
           for (var i = 0;i < self.resolvedCb.length; i++) {
             self.resolvedCb[i](result)
           }
         }
       }
     
       var reject = function (err) {
         // 判断状态
         if (self.PromiseStatus === 'pending') {
            self.PromiseStatus = 'rejected';
            self.PromiseValue = err;
           
            // rejectedCb 队列依次执行
           for (var i = 0;i < self.rejectedCb.length; i++) {
             self.rejectedCb[i](result)
           }
         }
       }
     
     // 错误处理 -> rejected
      try {
        fn(resolve, reject)
      } catch(e) {
        reject(e)
      }
      
    }
    

    当然这还不够,因为重要的两个功能thencatch还没有实现。

    从现有的 then 分析


    分析下then的使用

    promise1.then(function(value){
       // todo
       return value;
    })
    .then(function(value1){
        // todo
        return value1;
    })
    .then(function(value2){
      // todo
      return value2;
    })
    
    • promise1 返回的值 需要塞到第一个then中函数的value上
    • 链式调用,多次调用
    • then返回的是一个新的Promise
    • then可以接受2个函数作为参数,一个是成功函数,一个是失败函数
    • return 的值 直接作为下个 then 中匿名函数的入参

    根据Promise返回的实例,我们可看出来then是挂载在 Promise原型链上。

    我们先实现一个大体的框架:

    Promise.prototype.then = function (handleSuccess, handleFail) {
        var self = this;
        var PromiseStatus = this.PromiseStatus;
    
        if(typeof handleSuccess === 'function') {
          handleSuccess = handleSuccess;
        } else {
          handleSuccess = function (result) {}
        }
    
        if(typeof handleFail === 'function') {
          handleFail = handleFail;
        } else {
          handleFail = function (err) {}
        }
    
        if(PromiseStatus === 'pending') {
            return new Promise(function(resolve, reject) {
              self.resolvedCb.push(handleSuccess);
              self.rejectedCb.push(handleFail);
            })
        }
    
        if(PromiseStatus === 'resolved') {
            return new Promise(function(resolve, reject) {
              var result = handleSuccess(self.PromiseValue);
              resolve(result);
            })
        }
    
        if(PromiseStatus === 'rejected') {
          return new Promise(function(resolve, reject) {
            var result = handleFail(self.PromiseValue);
            reject(result);
          })
        }
        
    }
    

    我们先用一下,看下是否符合期望

    方式一(无异步操作):

    function promise1() {
      return new Promise(function(resolve, reject){
          console.log('执行promise1')
          resolve('zqz');
      })
    }
    
    promise1().then(function(result){
      console.log('执行1', 'result:'+result)
      return result + '11';
    })
    .then(function(result){
        console.log('执行2', 'result:'+result)
      return result + '22';
    })
    // => 执行promise1
    // => 执行1 result:zqz
    // => 执行2 result:zqz11
    // => Promise {PromiseStatus: "resolved", PromiseValue: "zqz1122", resolvedCb: Array(0), rejectedCb: Array(0)}
    

    这样使用没有问题!

    方式二(有异步操作):

    function promise1() {
      return new Promise(function(resolve, reject){
        // 异步操作
        setTimeout(function(){
          console.log('执行promise1')
          resolve('zqz');
        },1000)
    
      })
    }
    
    promise1().then(function(result){
      console.log('执行1', 'result:'+result)
      return result + '11';
    })
    .then(function(result){
        console.log('执行2', 'result:'+result)
      return result + '22';
    })
    // => 执行promise1
    // => 执行1 result:zqz
    

    一旦出现异步操作,就有问题!很明显,Promise的主要作用就是控制异步操作的执行顺序。

    肯定是哪里有问题,我们来分析一下,异步的时候 有哪些 不同

    • 当有异步操作(xhr,setTimeout等)的时候,这时候PromiseStatuspending状态

    在来看下我们在pending时候的处理

    ...
    // 异步时
    if(PromiseStatus === 'pending') {
            return new Promise(function(resolve, reject) {
             // 这里只是将函数塞入队列,然后就没有然后来。。。这是有问题的
              self.resolvedCb.push(handleSuccess);
              self.rejectedCb.push(handleFail);
            })
        }
    ...
    

    这时候我们的两个数组:resolvedCbrejectedCb就发挥作用了,由于我们不知道异步什么时候结束,但是我们可以根据他们定义的先后顺序注入到 队列 中,然后根据 顺序 依次执行,这样也就保证了异步操作的执行顺序。

    if(PromiseStatus === 'pending') {
            return new Promise(function(resolve, reject) {
             // 一个个的塞入队列
              self.resolvedCb.push(function(result) {
                  var res = handleSuccess(self.PromiseValue);
                  resolve(res);
              })
              self.rejectedCb.push(function(err) {
                  var er = handleFail(self.PromiseValue);
                  reject(er);
              })
            })
        }
    

    这时候我们用多个异步操作来测试一下

    function async1() {
      return new Promise(function(resolve, reject){
        // 异步操作
        setTimeout(function(){
          console.log('执行async1')
          resolve('zqz1');
        },3000)
      })
    }
    function async2() {
      return new Promise(function(resolve, reject){
        // 异步操作
        setTimeout(function(){
          console.log('执行async2')
          resolve('zqz2');
        },1000)
      })
    }
    function async3() {
      return new Promise(function(resolve, reject){
        // 异步操作
        setTimeout(function(){
          console.log('执行async3')
          resolve('zqz3');
        },2000)
      })
    }
    
    // return 一个新的promise
    async1().then(function(result){
       console.log('result = ' + result)
       return async2();
    }).then(function(result){
       console.log('result = ' + result)
       return async3();
    }).then(function(result){
       console.log('result = ' + result)
       return result;
    })
    
    // => Promise {PromiseStatus: "pending", PromiseValue: "", resolvedCb: Array(0), rejectedCb: Array(0)}
    // => 执行async1
    // => result = zqz1
    // => result = [object Object]
    // => result = [object Object]
    // => 执行async2
    // => 执行async3
    

    这里有两个问题:

    • 返回promise的时候,执行顺序有问题
    • 返回promise的时候,无法进行值的传递

    我们再来分析下,着重看下下面这块代码

    ...
    if(PromiseStatus === 'pending') {
            return new Promise(function(resolve, reject) {
              self.resolvedCb.push(function(result) {
                  // 这里返回的res有可能是promise,但是我们没有做处理
                  var res = handleSuccess(self.PromiseValue);
                  resolve(res);
              })
              self.rejectedCb.push(function(err) {
                  // 这里返回的res有可能是promise,但是我们没有做处理
                  var er = handleFail(self.PromiseValue);
                  reject(er);
              })
            })
        }
    ...
    

    因为我们返回的是Promise,由于我们没有做处理,导致无法正确的获取到值。

    ...
    if(PromiseStatus === 'pending') {
            return new Promise(function(resolve, reject) {
              self.resolvedCb.push(function(result) {
                  var res = handleSuccess(self.PromiseValue);
                  if (res instanceof Promise) {
                       res.then(resolve, reject);
                  } else {
                       resolve(res);
                  } 
              })
              self.rejectedCb.push(function(err) {
                  var er = handleFail(self.PromiseValue);
                  if (er instanceof Promise) {
                       er.then(resolve, reject);
                  } else {
                       reject(er);
                  }
              })
            })
        }
    ...
    

    如果返回的是一个Promise,就继续塞入到then里面。

    在执行一下:

    async1().then(function(result){
       console.log('result = ' + result)
       return async2();
    }).then(function(result){
       console.log('result = ' + result)
       return async3();
    }).then(function(result){
       console.log('result = ' + result)
       return result;
    })
    
    // => Promise {PromiseStatus: "pending", PromiseValue: "", resolvedCb: Array(0), rejectedCb: Array(0)}
    // => 执行async1
    // => result = zqz1
    // => 执行async2
    // => result = zqz2
    // => 执行async3
    // => result = zqz3
    

    最后一个简单完整的 then:

    Promise.prototype.then = function (handleSuccess, handleFail) {
        var self = this;
        var PromiseStatus = this.PromiseStatus;
    
        if(typeof handleSuccess === 'function') {
          handleSuccess = handleSuccess;
        } else {
          handleSuccess = function (result) {}
        }
    
        if(typeof handleFail === 'function') {
          handleFail = handleFail;
        } else {
          handleFail = function (err) {}
        }
    
        if(PromiseStatus === 'pending') {
            return new Promise(function(resolve, reject) {
              self.resolvedCb.push(function(result) {
                  var res = handleSuccess(self.PromiseValue);
                  if (res instanceof Promise) {
                       res.then(resolve, reject);
                  } else {
                      resolve(er);
                  } 
              })
              self.rejectedCb.push(function(err) {
                  var er = handleFail(self.PromiseValue);
                  if (er instanceof Promise) {
                       er.then(resolve, reject);
                  } else {
                      reject(er);
                  } 
              })
            })
        }
        if(PromiseStatus === 'resolved') {
            return new Promise(function(resolve, reject) {
              var result = handleSuccess(self.PromiseValue);
              resolve(result);
            })
        }
        if(PromiseStatus === 'rejected') {
          return new Promise(function(resolve, reject) {
            var result = handleFail(self.PromiseValue);
            reject(result);
          })
        }
        
    }
    
    
    

    参考


  • 相关阅读:
    解决 minwidth 在 IE6 中无效的方法
    SmallSlider 图片轮播插件
    css li中a的高端与li的高端不一致,解决方案
    为什么height:5px在IE6里显示不正常?
    命令提示符下不能输入中文
    Sql中日期差 Sql中 DateDiff Sql 中时间差
    20100420 18:17 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\aspnet_state\Parameters
    多个表结果的并列显示
    linux系统下载地址
    VMware中Shared Folders(共享文件夹)的配置
  • 原文地址:https://www.cnblogs.com/zqzjs/p/8017497.html
Copyright © 2020-2023  润新知