本文已整顿到 Github,地址  blog


实现 Promise/A+

本文的指标是编写一个与 then/promise 相似的合乎 Promise/A+ 的实现。

以下前半部分译自 Implementing promises from scratch,也是本文的重点。你能够查看原文,它还应用 TDD 形式,编写一些测试用例,帮忙你了解。下半局部是 Promise 各个办法的实现。

Promise 状态

Promise 是必须处于以下状态之一的对象/函数:PENDINGFULFILLEDREJECTED,最后 promise 处于 PENDING 状态。

Promise 能够从 PENDING 状态转换为带 value 值的 FULFILLED 状态或带 reasonREJECTED 状态。

为了进行状态转换,promise 构造函数接管到一个名为 executor 的函数,executor 会立刻被调用,调用时应用两个函数 fulfillreject 来执行状态转换:

  • fulfill(value) — 通过 valuePENDINGFULFILLEDvalue 当初是 promise 的属性。
  • reject(reason) — 通过 reasonPENDINGREJECTEDreason 当初是 promise 的属性。

最后的实现很简略:

// 可能的状态const PENDING = 'PENDING'const FULFILLED = 'FULFILLED'const REJECTED = 'REJECTED'class APromise {  constructor(executor) {    // 初始化状态    this.state = PENDING    // 胜利的 value 或回绝的 reason 在外部映射为 value,最后 promise 没有值    // 调用立刻执行程序    doResolve(this, executor)  }}// 带 value 的 fulfillfunction fulfill(promise, value) {  promise.state = FULFILLED  promise.value = value}// 带 reason 的 rejectfunction reject(promise, reason) {  promise.state = REJECTED  promise.value = reason}// 创立作为 executor 参数的 fulfill/reject  函数function doResolve(promise, executor) {  function wrapFulfill(value) {    fulfill(promise, value)  }  function wrapReject(reason) {    reject(promise, reason)  }  executor(wrapFulfill, wrapReject)}

察看状态变动

为了察看 promise 状态的变动(以及胜利的值或回绝的起因),咱们应用 then 办法,该办法接管两个参数,一个 onFulfilled 函数和一个 onRejected 函数,调用这些函数的规定如下:

  • 当 promise 处于 FULFILLED 状态时,onFulfilled 函数将被调用,并带有 promise 履行的 value,例如 onFulfilled(value)
  • 当 promise 处于 REJECTED 状态时,onRejected 函数将被调用,并带有 promise 被回绝的 reason,例如 onRejected(reason)

咱们将这些函数称为 promise handlers(译为处理程序)。

让咱们将 then 函数增加到类原型中,留神它会依据 Promise 的状态调用 onFulfilledonRejected 函数。

class APromise {  // ...  then(onFulfilled, onRejected) {    handleResolved(this, onFulfilled, onRejected)  }  // ...}function handleResolved(promise, onFulfilled, onRejected) {  const cb = promise.state === FULFILLED ? onFulfilled : onRejected  cb(promise.value)}

单向转换

一旦转换到其中一个 FULFILLEDREJECTED 状态,promise 不得转换到任何其余状态。

在咱们以后的实现中,调用 executor 的函数应该确保只调用一次 fulfillreject,后续调用应该被疏忽

function doResolve(promise, executor) {  let called = false  function wrapFulfill(value) {    if (called) return    called = true    fulfill(promise, value)  }  function wrapReject(reason) {    if (called) return    called = true    reject(promise, reason)  }  executor(wrapFulfill, wrapReject)}

解决 executor 谬误

如果执行 executor 失败,promise 应转换到 REJECTED 状态,并阐明失败起因

function doResolve(promise, executor) {  // ...  try {    executor(wrapFulfill, wrapReject)  } catch (err) {    wrapReject(err)  }}

异步 executor

如果解析器的 fulfill/reject 是异步执行,则咱们的 .then 办法将失败,因为它的 handlers 将立刻执行。

让咱们向 Promise 增加一个队列,它的目标是存储一旦 Promise 状态从 PENDING 更改为其余状态时将调用的 handlers,同时咱们的 .then 办法应该查看 Promise 状态,以决定是立刻调用 handler 还是存储 handler,让咱们将此逻辑挪动到新的辅助函数 handle

class APromise {  constructor(executor) {    this.state = PENDING    // 存储 .then handler 队列    this.queue = []    doResolve(this, executor)  }  then(onFulfilled, onRejected) {    handle(this, { onFulfilled, onRejected })  }}// 查看 promise 的状态:// - 如果 promise 为 PENDING,将其推入 queue 以供当前应用// - 如果 promise 还不是 PENDING,则调用 handlerfunction handle(promise, handler) {  if (promise.state === PENDING) {    // 如果为 PENDING,推入 queue    promise.queue.push(handler)  } else {    // 立刻执行    handleResolved(promise, handler)  }}function handleResolved(promise, handler) {  const cb =    promise.state === FULFILLED ? handler.onFulfilled : handler.onRejected  cb(promise.value)}

此外,应该更新 fulfillreject 办法,以便在调用时调用 Promise 中存储的所有 handlers,这将在更新状态和值后调用的新函数 finale 中实现。

function fulfill(promise, value)  // ...  finale(promise)}function reject(promise, reason) {  // ...  finale(promise)}// 调用 promise 中存储的所有 handlersfunction finale(promise) {  const length = promise.queue.length  for (let i = 0; i < length; i += 1) {    handle(promise, promise.queue[i])  }}

链式的 Promise

咱们的 .then 办法应该返回一个新的 Promise。

实现也很简略,然而咱们将看到新的 Promise 以不同于应用 executor 的形式转换到不同的状态,新的 Promise 应用 handlers 进行转换,如下所示:

  • 如果 onFulfilledonRejected 函数被调用

    • 如果执行时没有谬误,Promise 将转换为 FULFILLED 状态,返回值作为 value
    • 如果执行时呈现谬误,Promise 将转换到 REJECTED 状态,并将谬误作为 reason

让咱们做一个 .then 办法首先返回 Promise:

class APromise {  // ...  then(onFulfilled, onRejected) {    // 空的 executor    const promise = new APromise(() => {})    handle(this, { onFulfilled, onRejected })    return promise  }}

对于实现,咱们首先必须将新的 Promise 也存储在 handler 队列中,这样,如果察看到的 Promise 被解析,那么队列中的元素就晓得须要解析哪个 Promise。

class APromise {  // ...  then(onFulfilled, onRejected) {    const promise = new APromise(() => {})    // 同时保留 promise    handle(this, { promise, onFulfilled, onRejected })    return promise  }}function handleResolved(promise, handler) {  const cb =    promise.state === FULFILLED ? handler.onFulfilled : handler.onRejected  // 执行 handler 并依据规定进行转换  try {    const value = cb(promise.value)    fulfill(handler.promise, value)  } catch (err) {    reject(handler.promise, err)  }}

异步 handlers

接下来,让咱们思考 handler 返回 Promise 的状况,在这种状况下,作为 handler 一部分的 Promise(不是返回的 Promise)应该采纳返回 Promise 的状态履行值或回绝起因。

让咱们构想以下场景:

const executor = fulfill => setTimeout(fulfill, 0, 'p')const p = new APromise(executor)const qOnFulfilled = value =>  new APromise(fulfill => fulfill(value + 'q'))const q = p.then(qOnFulfilled)const rOnFulfilled = value => (  // 值应为 pq)const r = q.then(rOnFulfilled)

在咱们以后的实现中,元组 { q, qOnFulfilled } 存储在 p 的 handlers 中,并且咱们确信在 q 存储元组 { r, rOnFulfilled } 之前,qOnFulfilled 被调用,咱们能够利用这一事实,并检测 handler 何时返回一个 Promise,在返回的 Promise 中存储观察者,例如在 qOnFulfilled 返回的 Promise 上存储 { r, onFulfilled }

请留神,咱们应用的是 while,因为嵌套的 Promise 自身可能有另一个 Promise 作为解析值。

function handle(promise, handler) {  // 取最深处的 promise 的状态  while (promise.value instanceof APromise) {    promise = promise.value  }  // ...}

其余状况

有效的 handlers

如果本来应该是函数的 handler 不是函数,那么咱们的实现就会失败:

const p = new APromise((fulfill) => fulfill('p'))const qOnFulfilled = nullconst q = p.then(qOnFulfilled)

在这种状况下,q 应该立刻用 p 的值进行解析

function handleResolved(promise, handler) {  const cb =    promise.state === FULFILLED ? handler.onFulfilled : handler.onRejected  // 如果 handler 不是函数,则立刻解析  if (typeof cb !== 'function') {    if (promise.state === FULFILLED) {      fulfill(handler.promise, promise.value)    } else {      reject(handler.promise, promise.value)    }    return  }  // ...}

在事件循环之后执行 handlers

要求 2.2.4,正如 3.1 中指出的,handlers 被一个新的堆栈调用,此外,即便 executor/handlers 是同步的,也能够确保未来调用观察者,从而使 Promise 解析保持一致。

咱们能够应用任何容许咱们在事件循环之后调用函数的函数,这包含 setTimeoutsetImmediaterequestAnimationFrame

function handleResolved(promise, handler) {  setImmediate(() => {    // ...  })}

以已解决的 Promise 为理由回绝

要求 2.2.7.2,只有当 promise 不处于 REJECTED 状态时才采纳嵌套 promise 的状态。

function handle(promise, handler) {  // 以返回的 promise 的状态为例  while (promise.state !== REJECTED && promise.value instanceof APromise) {}  // ...}

Promise 自身无奈解决

要求 2.3.1,在 fulfill 办法上,让咱们查看履行值是否等于 Promise 自身,如果是这样,则抛出一个 TypeError

function fulfill(promise, value) {  if (value === promise) {    return reject(      promise,      new TypeError('A promise cannot be resolved with itself.')    )  }  // ...}

Thenable

2.3.3.3 相干要求,handler 的返回值可能是一个 thenable,一个 object/function,它具备一个可拜访的 then 属性,这是一个函数,then 函数就像一个 executor,它接管一个 fulfillreject 回调,应该用来转换 thenable 的状态。

让咱们批改 fulfill 办法并增加对 thenable 的查看,留神拜访属性并不总是平安的操作(例如,属性可能应用 getter),这就是为什么咱们应该将它包装在 try/catch 中。

另外,thenable 的 then 应该被调用为 this

function fulfill(promise, value) {  if (value === promise) {    return reject(      promise,      new TypeError('A promise cannot be resolved with itself.')    )  }  if (value && (typeof value === 'object' || typeof value === 'function')) {    let then    try {      then = value.then    } catch (err) {      return reject(promise, err)    }    // promise    if (then === promise.then && promise instanceof APromise) {      promise.state = FULFILLED      promise.value = value      return finale(promise)    }    // thenable    if (typeof then === 'function') {      return doResolve(promise, then.bind(value))    }  }  // primitive  promise.state = FULFILLED  promise.value = value  finale(promise)}

实现 Promise.catch()

class APromise {  // ...  catch(onRejected) {    return this.then(null, onRejected)  }}

实现 Promise.finally()

Promise.prototype.finally() 返回一个 Promise。在 promise 完结时,无论后果是 fulfilled 或者是 rejected,都会执行指定的回调函数。这为在 Promise 是否胜利实现后都须要执行的代码提供了一种形式。

将返回 promise 的后果在 then 一次,无论返回的是 fulfilled 还是 rejected,都执行给定的回调函数。

class APromise {  finally(onFinally) {    return this.then(      /* onFulfilled */      (res) => Promise.resolve(onFinally().call(this)).then(() => res),      /* onRejected */      (err) =>        Promise.resolve(onFinally().call(this)).then(() => {          throw err        })    )  }}

示例:

new APromise((resolve, reject) => {  resolve('ok')})  .then((res) => {    console.log(res) // ok  })  .finally(() => {    console.log('finally') // finally  })

实现 Promise.resolve()

外部返回一个 promise,调用 resolve 办法,将 value 参数传入

class APromise {  // ...  static resolve(value) {    return new APromise((resolve) => resolve(value))  }}

实现 Promise.reject()

外部返回了一个 promise,调用 reject 办法,将 reason 参数传入

class APromise {  // ...  static reject(reason) {    return A((resolve, reject) => reject(reason))  }}

实现 Promise.race()

Promise.racePromise.all 相似,但只期待第一个 settled 的 promise 并获得其 result/error,第一个 settled promise 之后,所有其余的 result/error 都会被疏忽。

class APromise {  // ...  static race(arr) {    const _Promise = this    if (!Array.isArray(arr)) {      return _Promise.reject(new TypeError('race() only accepts an array'))    }    return new _Promise((resolve, reject) => {      arr.forEach((p) => {        _Promise.resolve(p).then(resolve, reject)      })    })  }}

示例:

const sleep = (sm) =>  new APromise((resolve) => setTimeout(() => resolve(sm), sm))const err = (ms) => sleep(ms).then(() => APromise.reject(ms))APromise.race([1, 2, 3]).then(console.log) // 1APromise.race([sleep(300), sleep(100), sleep(200)]).then(console.log) // 100APromise.race([sleep(3000), err(100), sleep(2000)]).catch(console.error) // 100APromise.race([err(50), err(60)]).catch(console.error) // 50

实现 Promise.all()

  • 如果传入的可迭代对象内的 promise 全副胜利,那么就返回 resolve 胜利的数组
  • 一旦有一个 promise 执行失败,Promise.all 间接返回谬误的那个 reject
class APromise {  // ...  static all(promises) {    let remaining = promises.length    // 判断是否为空    if (remaining === 0) return APromise.resolve([])    return new APromise((resolve, reject) => {      promises.reduce((acc, promise, i) => {        APromise.resolve(promise).then(          (res) => {            acc[i] = res            --remaining || resolve(acc)          },          (err) => {            reject(err)          }        )        return acc      }, [])    })  }}

测试:

const sleep = (sm) =>  new APromise((resolve) => setTimeout(() => resolve(sm), sm))const err = (ms) => sleep(ms).then(() => APromise.reject(ms))APromise.all([1, 2, 3]).then(console.log) // [1, 2, 3]APromise.all([sleep(300), sleep(100), sleep(200)]).then(console.log)APromise.all([sleep(3000), err(100), sleep(2000)]).catch(console.error) // 100APromise.all([err(50), err(60)]).catch(console.error) // 50

实现 promise.any()

Promise.any() 接管一个 Promise 可迭代对象,只有其中的一个 promise 胜利,就返回那个曾经胜利的 promise。如果可迭代对象中没有一个 promise 胜利(即所有的 promises 都失败/回绝),就返回一个失败的 promiseAggregateError 类型的实例。

Promise.any 的行为跟 Promise.all 刚好相同:

  • 只有有一个胜利,那么就立刻 resolve 进来
  • 如果全副失败,那么就将所有 reject 后果收集起来并返回 AggregateError
class APromise {  // ...  static any(promises) {    return A((resolve, reject) => {      if (promises.length === 0)        return reject(new AggregateError('All promises were rejected'))      promises.reduce((acc, cur) => {        Promise.resolve(cur).then(          (data) => {            resolve(data)          },          (err) => {            acc.push(err)            if (acc.length === promises.length)              reject(new AggregateError('All promises were rejected'))          }        )        return acc      }, [])    })  }}

示例:

const sleep = (sm) =>  new APromise((resolve) => setTimeout(() => resolve(sm), sm))const err = (ms) => sleep(ms).then(() => APromise.reject(ms))APromise.any([1, 2, 3]).then((o) => console.log(o)) // 1APromise.any([sleep(3000), err(100), sleep(2000)]).then(console.info) // 2000APromise.any([err(50), err(60)]).catch(console.log) // AggregateError

实现 Promise.allSettled()

Promise.allSettled() 办法返回一个在所有给定的 promise 都曾经 fulfilled 或 rejected 后的 promise,并带有一个对象数组,每个对象示意对应的 promise 后果。
class APromise {  // ...  static allSettled(values) {    let promises = [].slice.call(values)    return new APromise((resolve, reject) => {      let result = [],        count = 0      promises.forEach((promise) => {        APromise.resolve(promise)          .then((value) => {            result.push({ status: 'fulfilled', value })          })          .catch((err) => {            result.push({ status: 'rejected', value: err })          })          .finally(() => {            if (++count === promises.length) {              resolve(result)            }          })      })    })  }}

示例:

const sleep = (sm) =>  new APromise((resolve) => setTimeout(() => resolve(sm), sm))const err = (ms) => sleep(ms).then(() => APromise.reject(ms))APromise.allSettled([1, 2, 3]).then(console.log)APromise.allSettled([sleep(300), sleep(100), sleep(200)]).then(console.log)APromise.allSettled([sleep(3000), err(100), sleep(2000)]).catch(console.error)APromise.allSettled([err(50), err(60)]).then(console.log)

更多材料

以上 Promise/A+ 残缺示例。

  • JavaScript Promise 迷你书(中文版)
  • Promises/A+
  • Promises/A 与 Promises/A+ 的区别
  • Conformant Implementations
  • 通过从头开始构建 Promise 来学习 JavaScript Promise
  • implementing
  • Optimization killers
  • es6-promise
  • 性能晋升优化技巧
  • 对于异步堆栈跟踪