zhaoqize/blog

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

zhaoqize opened this issue · 2 comments

写在前面

这个文章,展现的是一个实现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);
      })
    }
    
}

参考

看完你的promise,感觉好牛逼呀! 简单大气,自己手敲一遍学习学习。

@wcx521 谢谢您的赞赏,互相学习!