一、规范
首先看看 Promise/A+ 规范要求:
Promise 代表着异步操作的最终结果。与 promise 进行交互的主要方式是通过then方法, 该方法通过注册回调以接收 promise 的最终值或 promise 未完成的原因。
-
promise 状态
pormise 必须是以下三个状态之一:pending,fulfilled,rejected。
- 当 promise 处于 pending 状态时:
- 可以转换到 fulfilled 或 rejected 状态。
- 当 promise 处于 fulfilled 状态时:
- 不能转换到其他状态。
- 必须有一个 value,并且不能改变。
- 当 promise 处于 rejected 状态时:
- 不能转换到其他状态。
- 必须有 reason ,并且不能改变。
- 当 promise 处于 pending 状态时:
-
then 方法
promise 必须提供一个 then 方法,能由此去访问当前或最终的 value 或者 reason。
pormise 的 then 方法, 接受两个参数
promise.then(onFulfilled, onRejected)-
onFulfilled和onRejected都是可选参数。- 如果
onFulfilled不是函数,则忽略。 - 如果
onRejected不是函数,则忽略。
- 如果
-
如果
onFulfilled是一个函数:- 它必须在
promise被fulfilled后,以promise的value作为第一个参数调用。 - 它不能在
promise被fulfilled之前调用。 - 它不能被调用多次。
- 它必须在
-
如果
onRejected是一个函数:- 它必须在
promise被rejected后,以promise的reason作为第一个参数调用。 - 它不能能在
promise被rejected之前调用。 - 它不能被调用多次。
- 它必须在
-
在 execution context 栈(执行上下文栈)只包含引擎代码之前,
onFulfilled或者onRejected不能被调用. -
onFulfilled或者onRejected必须以函数形式调用(即不能有this值) -
then方法可以被同一个promise调用多次。- 当
promise处于fulfilled状态, 所有自己的onFulfilled回调函数,必须要按照then注册的顺序被调用。 - 当
promise处于rejected状态, 所有自己的onRejected回调函数,必须要按照then注册的顺序被调用。
- 当
-
then方法必须要返回promisepromise2 = promise1.then(onFulfilled, onRejected);- 如果
onFulfilled或者onRejected返回一个值x,则执行 Promise Resolution Procedure[[Resolve]](promise2, x). - 如果
onFulfilled或者onRejected抛出异常e,promise2必须以e作为 reason ,转到 rejected 状态。 - 如果
onFulfilled不是函数,并且promise1处于 fulfilled 状态 ,则promise2必须以与promise1同样的 value 被 fulfilled . - 如果
onRejected不是函数,并且promise1处于 rejected 状态 ,则promise2必须以与promise1同样的 reason 被 rejected .
- 如果
-
-
Promise Resolution Procedure
Promise Resolution Procedure 是一个抽象操作。它以一个 promise 和一个 value 作为输入,记作:
[[Resolve]](promise, x)。 如果x是一个 thenable , 它会尝试让 promise 变成与 x 的一样状态 ,前提 x 是一个类似的 promise 对象。否则,它会让 promise 以x作为 value 转为 fulfilled 状态。这种对 thenables 的处理允许不同的 promise 进行互操作,只要它们暴露一个符合 Promises/A+ 的 then 方法。它还允许 Promises/A+ 实现使用合理的 then 方法“同化”不一致的实现。
[[Resolve]](promise, x)执行以下步骤:-
如果
promise和x引用的是同一个对象,则以一个TypeError作为 reason 让promise转为 rejeted 状态。 -
如果
x也是一个 promise ,则让promise接受它的状态- 如果
x处于 pending 状态,promise必须保持 pending 状态,直到x变成 fulfilled 或者 rejected 状态,promise才同步改变。 - 如果或者当
x处于 fulfilled 状态, 以同样的 value 让promise也变成 fulfilled 状态。 - 如果或者当
x处于 rejected 状态, 以同样的 reason 让promise也变成 rejected 状态。
- 如果
-
如果
x是一个对象或者函数。-
令
then等于x.then. -
如果读取
x.then抛出异常e, 以e作为 reason 让promise变成 rejected 状态。 -
如果
then是一个函数,以x作为this调用它,传入第一个参数resolvePromise, 第二个参数rejectPromise。-
如果
resolvePromise被传入y调用, 则执行[[Resolve]](promise, y) -
如果
rejectedPromise被传入r调用,则用,r作为 reason 让promise变成 rejected 状态 -
如果
resolvePromise和rejectPromise都被调用了,或者被调用多次了。只有第一次调用生效,其余会被忽略。 -
如果调用
then抛出异常e,- 如果
resolvepromise或rejectPromise已经被调用过了,则忽略它。 - 否则, 以
e作为 reason 让promise变成 rejected 状态。
- 如果
-
-
如果
then不是一个函数,以x作为 value 让promise变成 fulfilled 状态。
-
-
如果
x不是对象或函数, 以x作为 value 让promise变成 fulfilled 状态。
-
如果一个 promise 被一个循环的 thenable 链中的对象 resolved,而 [[Resolve]](promise, thenable) 的递归性质又使得其被再次调用,根据上述的算法将会陷入无限递归之中。算法虽不强制要求,但也鼓励实现者检测这样的递归是否存在,并且以 TypeError 作为 reason 拒绝 promise。
二、实现
- 使用 class 实现 Promise 类
- 使用 class 私有字段
- 使用
queueMicrotask取代setTimeout实现异步微任务
// promise.js
/* promise/A+ 规范 */
class Promise {
static #FULFILLED = 'fulfilled'
static #REJECTED = 'rejected'
static #PENDING = 'pending'
static #resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) {
throw new TypeError('Chaining cycle detected for promise')
}
if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
let called
try {
const then = x.then
if (typeof then !== 'function') resolve(x)
else {
then.call(
x,
(value) => {
if (called) return
called = true
Promise.#resolvePromise(promise2, value, resolve, reject)
},
(reason) => {
if (called) return
called = true
reject(reason)
}
)
}
} catch (e) {
if (called) return
called = true
reject(e)
}
} else {
resolve(x)
}
}
#state = Promise.#PENDING
#result = null
#onResolveCallbacks = []
#onRejectCallbacks = []
constructor(executor) {
if (typeof executor !== 'function') {
return new TypeError(`Promise resolver ${executor} is not a function`)
}
try {
executor(this.#resolve.bind(this), this.#reject.bind(this))
} catch (e) {
this.#reject(e)
}
}
#resolve(value) {
if (this.#state === Promise.#PENDING) {
this.#state = Promise.#FULFILLED
this.#result = value
this.#onResolveCallbacks.forEach((cb) => cb())
}
}
#reject(reason) {
if (this.#state === Promise.#PENDING) {
this.#state = Promise.#REJECTED
this.#result = reason
this.#onRejectCallbacks.forEach((cb) => cb())
}
}
then(onFulfilled, onRejected) {
if (typeof onFulfilled !== 'function') {
onFulfilled = (value) => value
}
if (typeof onRejected !== 'function') {
onRejected = (reason) => {
throw reason
}
}
const promise2 = new Promise((resolve, reject) => {
if (this.#state === Promise.#PENDING) {
this.#onResolveCallbacks.push(() => {
queueMicrotask(() => {
try {
const x = onFulfilled(this.#result)
Promise.#resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
})
this.#onRejectCallbacks.push(() => {
queueMicrotask(() => {
try {
const x = onRejected(this.#result)
Promise.#resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
})
}
if (this.#state === Promise.#FULFILLED) {
queueMicrotask(() => {
try {
const x = onFulfilled(this.#result)
Promise.#resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
}
if (this.#state === Promise.#REJECTED) {
queueMicrotask(() => {
try {
const x = onRejected(this.#result)
Promise.#resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
}
})
return promise2
}
}
// 实现以下功能,并且执行 npx promises-aplus-tests
Promise.defer = Promise.deferred = function () {
let dfd = {}
dfd.promise = new Promise((resolve, reject) => {
dfd.resolve = resolve
dfd.reject = reject
})
return dfd
}
module.exports = Promise
三、验证
- 将文件保存为
promise.js - 在目录执行以下命令验证
$ npx promises-aplus-tests promise.js