ES6--浅析Promise内部结构

首发于:sau交流学习社区

一、前言

什么是promise?promsie的核心是什么?promise如何解决回调地狱的?等问题

1、什么是promise?promise是表示异步操作的最终结果;可以用来解决回调地狱和并发IO操作的问题

A promise represents the eventual result of an asynchronous operation.

2、promise 的核心是什么?promise的核心就是链式调用

3、采用什么方法可以实现链式调用?通过使用then的方法,then方法是用来注册在这个Promise状态确定后的回调,很明显,then方法需要写在原型链上。

4、promise是如何解决回调地狱的问题?(1)如果一个promise返回的是一个promise,会把这个promise传递结果传递到下一次的then中;(2)如果一个promise返回的是一个普通的值,会把这个普通值作为下一次then的成功回调结果;(3)如果当前promise失败了,会走下一个then的回调函数;(4)如果then不返回值,就会有一个默认值为undefined,作为普通值,会作为下一个then的成功回调;(5)catch是错误没有处理的情况才会执行;(6)then中可以不写东西

 

 

二、promise的标准解读

1、只有一个then方法,没有catchraceall等方法,甚至没有构造函数;

Promise标准中仅指定了Promise对象的then方法的行为,其它一切我们常见的方法/函数都并没有指定,包括catchraceall等常用方法,甚至也没有指定该如何构造出一个Promise对象,另外then也没有一般实现中(Q, $q等)所支持的第三个参数,一般称onProgress

2、then方法返回一个新的Promise;

Promise的then方法返回一个新的Promise,而不是返回this,此处在下文会有更多解释

promise2 = promise1.then(alert)
promise2 != promise1 // true

3、不同Promise的实现需要可以相互调用(interoperable)

4、Promise的初始状态为pending,它可以由此状态转换为fulfilled(本文为了一致把此状态叫做resolved)或者rejected,一旦状态确定,就不可以再次转换为其它状态,状态确定的过程称为settle

 

三、实现一个promise

1、构造函数

因为标准并没有指定如何构造一个Promise对象,所以我们同样以目前一般Promise实现中通用的方法来构造一个Promise对象,也是ES6原生Promise里所使用的方式,即:

// Promise构造函数接收一个executor函数,executor函数执行完同步或异步操作后,调用它的两个参数resolve和reject
var promise = new Promise(function(resolve, reject) {
  /*
    如果操作成功,调用resolve并传入value
    如果操作失败,调用reject并传入reason
  */
})

我们先实现构造函数的框架如下:

function Promise(executor) {
  var self = this
  self.status = 'pending' // Promise当前的状态
  self.data = undefined  // Promise的值
  self.onResolvedCallback = [] // Promise resolve时的回调函数集,因为在Promise结束之前有可能有多个回调添加到它上面
  self.onRejectedCallback = [] // Promise reject时的回调函数集,因为在Promise结束之前有可能有多个回调添加到它上面

  executor(resolve, reject) // 执行executor并传入相应的参数
}

上面的代码基本实现了Promise构造函数的主体,但目前还有两个问题:

1、我们给executor函数传了两个参数:resolve和reject,这两个参数目前还没有定义

2、executor有可能会出错(throw),类似下面这样,而如果executor出错,Promise应该被其throw出的值reject:

new Promise(function(resolve, reject) {
  throw 2
})

所以我们需要在构造函数里定义resolve和reject这两个函数:

function Promise(executor) {
  var self = this
  self.status = 'pending' // Promise当前的状态
  self.data = undefined  // Promise的值
  self.onResolvedCallback = [] // Promise resolve时的回调函数集,因为在Promise结束之前有可能有多个回调添加到它上面
  self.onRejectedCallback = [] // Promise reject时的回调函数集,因为在Promise结束之前有可能有多个回调添加到它上面

  function resolve(value) {
    // TODO
  }

  function reject(reason) {
    // TODO
  }

  try { // 考虑到执行executor的过程中有可能出错,所以我们用try/catch块给包起来,并且在出错后以catch到的值reject掉这个Promise
    executor(resolve, reject) // 执行executor
  } catch(e) {
    reject(e)
  }
}

有人可能会问,resolve和reject这两个函数能不能不定义在构造函数里呢?考虑到我们在executor函数里是以resolve(value)reject(reason)的形式调用的这两个函数,而不是以resolve.call(promise, value)reject.call(promise, reason)这种形式调用的,所以这两个函数在调用时的内部也必然有一个隐含的this,也就是说,要么这两个函数是经过bind后传给了executor,要么它们定义在构造函数的内部,使用self来访问所属的Promise对象。所以如果我们想把这两个函数定义在构造函数的外部,确实是可以这么写的:

function resolve() {
  // TODO
}
function reject() {
  // TODO
}
function Promise(executor) {
  try {
    executor(resolve.bind(this), reject.bind(this))
  } catch(e) {
    reject.bind(this)(e)
  }
}

但是众所周知,bind也会返回一个新的函数,这么一来还是相当于每个Promise对象都有一对属于自己的resolve和reject函数,就跟写在构造函数内部没什么区别了,所以我们就直接把这两个函数定义在构造函数里面了。不过话说回来,如果浏览器对bind的所优化,使用后一种形式应该可以提升一下内存使用效率。

另外我们这里的实现并没有考虑隐藏this上的变量,这使得这个Promise的状态可以在executor函数外部被改变,在一个靠谱的实现里,构造出的Promise对象的状态和最终结果应当是无法从外部更改的

接下来,我们实现resolve和reject这两个函数

function Promise(executor) {
  // ...

  function resolve(value) {
    if (self.status === 'pending') {
      self.status = 'resolved'
      self.data = value
      for(var i = 0; i < self.onResolvedCallback.length; i++) {
        self.onResolvedCallback[i](v
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值