New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
zhaoqize opened this Issue Nov 29, 2017 · 2 comments

Comments

Projects
None yet
2 participants
@zhaoqize
Owner

zhaoqize commented Nov 29, 2017

写在前面

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

参考

@zhaoqize zhaoqize changed the title from Promise原理与实现的探究 to Promise原理与实现探究的一种思路 Dec 10, 2017

@wcx521

This comment has been minimized.

wcx521 commented Dec 21, 2017

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

@zhaoqize

This comment has been minimized.

Owner

zhaoqize commented Jan 26, 2018

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment