如何实现一个Promise

Posted on 2018-05-27

简介

TODO: 简单介绍Promise/A+规范

实现

在实现Promise之前,先来看看Promise的简单调用

示例1:

new Promise(function (resolve) {
  resolve()
}).then(function (v) {
  console.log(v)
})

针对上述代码,可以很简单的给出下面的实现

function Promise (executor) {
	var value = null, callbacks = []
  
  this.then = function (onFulfilled) {
    callbacks.push(onFulfilled)
  }
  function resolve (v) {
    callbacks.forEach(cb => {
      cb(v)
    })
  }
  executor(resolve)
}
  • new Promise 时会立即执行传入的函数
  • 调用传入的函数时,会把Promise内部的函数resolve作为函数的参数
  • 执行传入的函数时,调用了函数的参数resolve,实际上会调用Promise内部的resolve函数
  • resolve函数会依次执行then方法传入的回调,此时then方法没有被执行,也就没有回调

所以可以设置延时执行resolve

示例2:

new Promise(function (resolve) {
  setTimeout(() => {
    resolve(10)
  }, 0)
}).then(function (v) {
  console.log(v)
})

设置setTimeout后,会优先执行后面的then方法,并将then方法的回调放入Promise的callbacks数组中,

通常在使用Promise会有多个then的回调,所有需要支持链式调用,修改resolve方法为:

function resolve(value) {
  setTimeout(function () {
    callbacks.forEach(cb => {
    	cb(value)
    })
  }, 0)
}

示例3:

new Promise(function (resolve) {
	resolve(10)
}).then(function (v) {
  console.log(v)
}).then(function (v) {
  console.log(v)
})

会输出两个10

如果Promise 异步操作已经成功,即调用了resolve函数,处于fulfilled状态时, 后面注册的then是不需要执行的。因此需要引入状态,Promise有三种状态:pending、fulfilled、rejected。初始状态为pending,并且只能从pending变为fulfilled和从pending变为rejected

function Promise (executor) {
	var value = null, callbacks = [], state = 'pending'
  
  this.then = function (onFulfilled) {
    if (state === 'pending') {
      callbacks.push(onFulfilled)
      return this
    }
    onFulfilled(value)
    return this
  }
  function resolve(newValue) {
    value = newValue
    state = 'fulfilled'
    setTimeout(function () {
      callbacks.forEach(cb => {
          cb(value)
      })
    }, 0)
  }
  executor(resolve)
}

resolve执行时,会将状态置为fulfilled,在此之后调用then方法都会立即执行。

在我们使用Promise过程中, 通常会在then方法中返回一个新的promise。这就是串行Promise,指当前 promise 达到 fulfilled 状态后,开始进行下一个 promise,修改then方法

this.then = function (onFulfilled) {
  return new Promise(function (resolve) {    
    handle({
      onFulfilled: onFulfilled || null,
      resolve: resolve
    })
  })
}
function handle (deferred) {
  if (state === 'pending') {
    callbacks.push(deferred)
    return
  }
  var ret = deferred.onFulfilled(value)
  deferred.resolve(ret)
}
  • then 方法中,创建了一个新的 Promise 实例,并作为返回值,这类 promise,权且称作 bridge promise,另外,因为返回类型一致,之前的链式执行仍然被支持;
  • handle 方法是当前 promise 的内部方法。这一点很重要,看不懂的童鞋可以去补充下闭包的知识。then 方法传入的形参 onFullfilled,以及创建新 Promise 实例时传入的 resolve 均被压入==当前 promise== 的 deferreds 队列中

新增的 handle 方法,相比改造之前的 then 方法,仅增加了一行代码:

deferred.resolve(ret)

这意味着当前 promise 异步操作成功后执行 handle 方法时,先执行then的回调 onFulfilled 方法,然后将其返回值作为实参执行 resolve 方法,标志新创建的promise进入fulfilled状态

接下来需要修改resolve方法

function resolve(newValue) {
  // 如果newValue存在且其类型为object或function
  if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) {
    var then = newValue.then // 获取newValue的then方法
    if (typeof then === 'function') {
      then.call(newValue, resolve)
      return
    }
  }
  value = newValue
  state = 'fulfilled'
  setTimeout(function () {
    callbacks.forEach(cb => {
        cb(value)
    })
  }, 0)
}

示例4:

var p = new Promise(function (resolve) {
  resolve(10)
}).then(function (v) {
  console.log(v)
  return 15
}).then(function (v) {
  console.log(v)
  return 20
})
p.then(function (v) {
  console.log(v)
})

上述代码会依次打印10, 15, 20

步骤分析:

  • new Promise 传入回调函数executor,会立即执行executor,并执行Promise内部的resolve函数,resolve会将value置为传入的值,并将Promise的状态标记为fulfilled状态,并执行异步任务中的callbacks的回调
  • then方法执行时,会新建一个Promise作为返回值,并执行handle,将then的回调和新的promise的resolve方法作为参数传递。该方法会判断一开始创建的promise的状态是否处于pending状态,由于已经调用了resolve(10) ,状态已处于fulfilled状态了,不执行条件语句内的代码。调用deferred.onFulfilled(value)实际上调用的就是then方法中定义的回调函数,并传入value值。
  • 第一个then方法的回调函数返回了15,即ret的值为15,并将ret作为形参调用我们的新创建的promise的resolve函数,由于15不是object或者function,所以直接执行非条件语句内的内容,将新创建的promise的value值置为15,状态置为fulfilled,并执行回调(此时没有then的回调)。

示例5:

var p = new Promise(function (resolve) {
  resolve(10)
}).then(function (v) {
  console.log(v)
  return new Promise(function (resolve) {
    resolve('a')
  }).then(function (v) {
    console.log(v)
    return 'b'
  })
}).then(function (v) {
  console.log(v)
  return 20
})
p.then(function (v) {
  console.log(v)
})

不同于示例4,在第一个then方法中返回了Promise实例。所以在执行到第一个then方法时:

  • 创建新的Promise, 传递then回调和resovle方法,调用handle函数
  • handle函数中,执行then的回调,并将返回值作为形参传递到resolve中,此时的返回值是一个Promise
  • 执行resolve,传入的参数是object类型,符合resolve中条件语句的判断,获取参数的then方法,如果then方法是function类型,则调用then方法,并传入resovle作为参数。