博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Promise原理与实现探究的一种思路
阅读量:7043 次
发布时间:2019-06-28

本文共 9234 字,大约阅读时间需要 30 分钟。

写在前面


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

参考


转载地址:http://aeqal.baihongyu.com/

你可能感兴趣的文章
实验吧 ---- 隐写术之so beautiful so white
查看>>
sklearn调包侠之线性回归
查看>>
《Linux命令行与shell脚本编程大全》 第四章
查看>>
Flask开发微电影网站(四)
查看>>
使用Maven搭建Springboot版ssm框架
查看>>
中介者模式
查看>>
怎么就死循环了!
查看>>
Jmeter之tomcat性能测试+性能改进措施
查看>>
MP实战系列(七)之集成springboot
查看>>
hexo 创建文章、标签、分类的Front-matter
查看>>
Confluence 6 复杂授权或性能问题
查看>>
从中国质造到淘宝心选:CBM赋能“数造”新品牌
查看>>
Python 学习笔记1
查看>>
python(logging )日志模块学习
查看>>
树莓派 之 爬虫(Scrapy)
查看>>
.Net外包篇:我是怎么看待外包的(二)
查看>>
A* 算法发明人 Nils Nilsson 逝世
查看>>
Netty 源码阅读入门实战(三)-服务端启动
查看>>
让年轻程序员少走弯路的14个忠告(引)
查看>>
BIO NIO AIO演变
查看>>