Promise/A+ 代码实现

352 阅读2分钟

Promise/A+ 规范

Promise/A+ 是关于 JS promise 的一个标准规范,ES6 promise实际上就是此规范的一个超集,理解这个规范能帮助你更好的理解ES6 promise

规范原文

我的中英对照翻译

实现代码

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

function PromiseDLL(fn) {
  this.state = PENDING
  this.value = null
  this.reason = null
  this.onFulfilled = null
  this.onRejected = null
  try {
    fn(this.resolve, this.reject)
  } catch (err) {
    this.reject(err)
  }
}

PromiseDLL.prototype.resolve = function (value) {
  if (this.state !== PENDING) {
    return
  }
  this.onFulfilled && this.onFulfilled(this.value)
  this.value = value
  this.state = FULFILLED
}

PromiseDLL.prototype.reject = function (reason) {
  if (this.state !== PENDING) {
    return
  }
  this.onRejected && this.onRejected(this.value)
  this.reason = reason
  this.state = REJECTED
}

PromiseDLL.prototype.then = function (onFulfilled, onRejected) {
  const that = this
  return new Promise((resolve, reject) => {
    function resolveP(x) {
      if (x === this) {
        throw TypeError('cannot be same object')
      } else {
        const then = x.then
        if ((typeof x === 'object' || typeof x === 'function') && typeof then === 'function') {
          then.call(x, resolve, reject)
        } else {
          resolve(x)
        }
      }
    }

    function fulfillHandler(value) {
      if (typeof onFulfilled === 'function') {
        resolveP(onFulfilled(value))
      } else {
        resolve(value)
      }
    }
    function rejectHandler(reason) {
      if (typeof onRejected === 'function') {
        resolveP(onRejected(reason))
      } else {
        reject(reason)
      }
    }
    switch (this.state) {
      case PENDING:
        that.onFulfilled = fulfillHandler
        that.onRejected = rejectHandler
        break
      case FULFILLED:
        setTimeout(() => {
          fulfillHandler(that.value)
        }, 0)
        break
      case REJECTED:
        setTimeout(() => {
          rejectHandler(this.reason)
        }, 0)
        break
      default:
        throw TypeError('undefined promise state')
    }
  })
}

Some Bulls**t

我本人的实现过程进展比较顺利,测试用例也一遍就全过了(偷偷开心),可能因为刚好前几天已经在写中英对照翻译的过程里通读了若干遍Promise/A+ 规范原文,但是一篇文章只放代码有点干巴巴,还是觉得要说一下巴拉巴会过瘾一点,所以以下写了一些实现过程中的思考:

  • 规范中拆分到多个章节的一些规则,其实是前后串联互相呼应的,比如对于onFulfilled/onRejected 抛错相关的描述,在实现中发现
    try {
      fn(this.resolve, this.reject)
    } catch (err) {
      this.reject(err)
    }
    
    这段代码已经可以 cover 住各种抛错的处理规则,所以不需要在实现then/resolve/reject方法时再额外去做异常处理了,这其实也说明Promise/A+规范本身是非常优雅的
  • Promise/A+ 规范中废弃了promise2 = promise1.then(...)promise2 !== promise1的要求(见Omissions - 1.3),这里在实现then方法时曾想过直接返回this来避免频繁创建新的Promise实例,但是当时思考到一半没有继续下去,后面如果有时间把这一部分想清楚再补上
  • then方法可以通过宏任务或微任务实现都可以(Promise/A+ 注释 3.1明确表示这里不做要求),这里使用了setTimeout来满足规则 2.2.4,但是如果希望基于微任务来实现,可以考虑使用其它方法(这里我预感到又可以写一篇关于宏任务微任务的废话

测试 Promise/A+

Promise/A+ 组织提供了官方测试用例,使用方式也非常简单,这里使用其提供的adapters 方式来测试,代码如下

// Adapter
PromiseDLL.deferred = () => {
  const res = {}
  res.promise = new Promise((resolve, reject) => {
    Object.assign(res, { resolve, reject })
  })
  return res
}

module.exports = PromiseDLL
// package.json
{
  ...
  "scripts": {
    ...
    "test": "promises-aplus-tests promise-aplus",
    ...
  },
  ...
}

872 个测试用例全部通过