promise 全网最详解释,包括各方法和手动实现Promises/A+ 规范(2)

接上一篇文章我们接着来说Promise
promise 全网最详解释,包括各方法和手动实现Promises/A+ 规范(1)

1.Promises/A+ 规范

规范的第一部分,描述了几个术语的意思。

  1. promise 是一个包含 then 方法的对象或函数,该方法符合规范指定的行为。
  2. thenable 是一个包含 then 方法和对象或者函数。
  3. value 就是任意合法 JS 值。
  4. exception 就是 throw 语句抛出的值。
  5. reason 是一个指示 promise 为什么被 rejected 的值。

Promise 状态
promise 有 3 个状态,分别是 pending, fulfilled 和 rejected。

  1. 在 pending 状态,promise 可以切换到 fulfilled 或 rejected。
  2. 在 fulfilled 状态,不能迁移到其它状态,必须有个不可变的 value。
  3. 在 rejected 状态,不能迁移到其它状态,必须有个不可变的 reason。
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
function Promise(){
  this.state = PENDING;
  this.result = null;
}
const transition = (promise,state,result) => {
  if(promise.state !== PENDING) return
  promise.state = state
  promise.result = result
}

落实到代码上,大概像上面那样:

有 3 个常量 pending, fulfilled, rejected,一个 Promise 构造函数,有 stateresult 两个属性。当 state 为 fulfilled 时,result 作为 value 看待。当 state 为 rejected 时,result 作为 reason 看待。一个 transition 状态迁移函数,它只会在 statepending 时,进行状态迁移。

Then 方法
promise 必须有 then 方法,接受 onFulfilledonRejected 参数。

Promise.prototype.then = function(onFulfilled,onRejected){

}

onFulfilledonRejected 如果是函数,必须最多执行一次。

onFulfilled 的参数是 valueonRejected 函数的参数是 reason

then 方法可以被调用很多次,每次注册一组 onFulfilled 和 onRejected 的 callback。它们如果被调用,必须按照注册顺序调用。

我们为上面得 Promise 新增一个 callbacks 数组记录。

function Promise(){
  this.state = PENDING;
  this.result = null;
  this.callbacks = [];
}

then 方法必须返回 promise。那 then 实现丰富化成下面这样:

Promise.prototype.then = function(onFulfilled,onRejected){
  return new Promise((resolve,reject) =>{
    let callback = {onFulfilled,onRejected,resolve,reject}
    if(this.state == PENDING){
      this.callbacks.push(callback)
    } else {
      setTimeout(()=>handleCallback(callback ,this.state,this.result),0)
    }
  })
}

在 then 方法里,return new Promise(f),满足 then 必须 return promise 的要求。

当 state 处于 pending 状态,就储存进 callbacks 列表里。

当 state 不是 pending 状态,就扔给 handleCallback 去处理。

handleCallback 是什么。不重要,我们只需要知道,它一定存在。我们得做一些处理,不是写死在 then 函数里,就是在外部的辅助函数里。为啥要套个 setTimeout?

因为我们不是在 JS 引擎层面实现 Promises,而是使用 JS 去实现 JS Promises。在JS里无法主动控制自身 execution context stack。可以通过 setTimeout/nextTick 等 API 间接实现,此处选用了 setTimeout。

then 方法返回的 promise,也有自己的 state 和 result。它们将由 onFulfilled 和 onRejected 的行为指定。这正是 handleCallback 要做的部分。

const handleCallback = (callback,state,result) => {
  let {onFulfilled,onRejected,resolve,reject} = callback
  try {
    if(state === FULFILLED){
      isFunction(onFulfilled) ? resolve(onFulfilled(result)):resolve(result)
    } else if(state === REJECTED) {
      isFunction(onRejected)?resolve(onRejected(result)):reject(result)
    }
  } catch (error) {
    reject(error)
  }
}

handleCallback 函数,根据 state 状态,判断是走 fulfilled 路径,还是 rejected 路径。

  1. 先判断 onFulfilled/onRejected 是否是函数,如果是,以它们的返回值,作为下一个 promise 的 result。
  2. 如果不是,直接以当前 promise 的 result 作为下一个 promise 的 result。
  3. 如果 onFulfilled/onRejected 执行过程中抛错,那这个错误,作为下一个 promise 的 rejected reason 来用。

then 方法核心用途是,构造下一个 promise 的 result。

const resolvePromise = (promise,result,resolve,reject) =>{
  if(result === promise){
    let reason = new TypeError('can not fufill promise with itself')
    return reject(reason)
  }
  if(isPromise(result)){
    return result.then(resolve,reject)
  }
  if(isThenable(result)){
    try{
      let then = result.then
      if(isFunction(then)){
        return new Promise(then.bind(result)).then(resolve,reject)
      }
    } catch (error) {
      return reject(error)
    }
  }
  resolve(result)
}

至此,所有重要部分,都已经被处理。

  1. 我们有了 transition 对单个 promise 进行状态迁移。
  2. 我们有了 handleCallback ,在当前 promise 和下一个 promise 之间进行状态传递。
  3. 我们有了 resolvePromise,对特殊的 result 进行特殊处理。

接下来,我们只需要整合一下,把各部分衔接起来即可。其中,Promise 构造函数,扩充如下:

function Promise(f){
  this.state = PENDING;
  this.result = null;
  this.callbacks = [];
  let onFulfilled = value => transition(this,FULFILLED,value)
  let onRejected = reason => transition(this,REJECTED,reason)

  let ignore = false
  let resolve = value => {
    if(ignore) return
    ignore = true
    resolvePromise(this,value,onFulfilled,onRejected)
  }
  let reject = reason => {
    if(ignore) return
    ignore = true
    onRejected(reason)
  }
  try{
    f(resolve,reject)
  } catch (error) {
    reject(error)
  }
}

构造 onFulfilled 去切换到 fulfilled 状态,构造 onRejected 去切换到 rejected 状态。

构造 resolve 和 reject 函数,在 resolve 函数里,通过 resolvePromise 对 value 进行验证。

配合 ignore 这个 flag,保证 resolve/reject 只有一次调用作用。

最后将 resolve/reject 作为参数,传入 f 函数。

若 f 函数执行报错,该错误就作为 reject 的 reason 来用。

const handleCallbacks = (callbacks,state,result) =>{
  while(callbacks.length) handleCallback(callbacks.shift(),state,result)
}
const transition = (promise,state,result) =>{
  if(promise.state !== PENDING) return
  promise.state = state
  promise.result = result
  setTimeout(() => handleCallbacks(promise.callbacks,state,result),0)
}

transition 函数扩充如上,当状态变更时,异步清空所有 callbacks。之前我们已经实现了 handleCallback,实现 handleCallbacks 只需要一个循环。

有了 then 方法,我们可以很容易实现 ES2015 Promises 的几个扩充方法。

Promise.prororype.catch = function(onRejected){
  return this.then(null,onRejected)
}
Promise.resolve = value => new Promise(resolve => resolve(value))
Promise.reject= reason => new Promise((_,reject)=> reject(reason))

catch 方法和 resolve/reject 静态方法的实现如上所示。Promise.all 和 Promise.race 的实现,留给感兴趣的读者,作为练习。

全部 passing。Promises/A+ 规范实现完毕。关于 Promises 还有好多话题可以讨论,比如它与函数式的 Monad 的关系,比如 Promise/A+ 规范中值得商榷和可以改进的部分等。
promise 全网最详解释,包括各方法和手动实现Promises/A+ 规范(2)_第1张图片

你可能感兴趣的:(教程,技术,js,javascript,前端)