实现一个 Promise(1)

符合 Promises/A+ 规范,即 then() 方法

Promise 是一个对象,用作 deferred 计算(可能是异步计算)的最终结果的 placeholder(占位符)。换句话说就是,一个 promise 代表了“异步操作”的最终结果。之所以一定是个“异步操作”,这取决于 promise 本身的执行机制,在事件循环里它属于微任务的一种。

Promise/A+

Promises/A+ 是 JavaScript promise 的开放标准,该规范详细介绍了 then 方法的行为。

作为与 promise 交互的主要方式,then 方法注册了两个回调,分别接收 promise 的正常返回值和出现异常的原因。

A. 相关术语

  1. promise 是一个对象或函数,且有一个 then 方法

  2. thenable 是一个对象或函数,用来定义 then 方法

  3. value 是 promise 成功状态时的值,可以是任何合法的 JavaScript 值(包括 undefined, thenable, promise)

  4. reason 是 promise 失败状态时的值,表示 promise 被拒绝的原因

  5. exception 是使用 throw 语句抛出的值

在 promise 正式成为语言标准之前,JavaScript 生态系统中存在过很多种不同的 promise 实现。尽管它们的内部表示有所差异,但所有的 promise-like objects 都实现了 thenable 接口,即 then() 方法。

promise 也是 thenable。为了与现有的 promise 实现互操作,语言通常允许用 thenable 代替 promise。比如:

const aThenable = {
    then(onFulfilled, onRejected) {
        onFulfilled({
            // The thenable is fulfilled with another thenable
            then(onFulfilled, onRejected) {
                onFulfilled(42)
            }
        })
    }
}

Promise.resolve(aThenable) // A promise fulfilled with 42

B. 要求

1. 三种状态

一个 promise 必须是以下三种状态之一:pending、fulfilled、rejected。且状态转移只能有两种:pending -> fulfilled、pending -> rejected。可以这样理解,promise 就是(承诺)其状态一旦改变,就永不可逆。

// 以下语句可以从 pending -> rejected
reject("oh, no!")
// reject(new Error("oh, no!"))

// throw ('oh, no!')
throw new Error('oh, no!')  // 注意:异步 throw,就和 uncaught error 一样

return Promise.reject('oh, no!')
// return Promise.reject(new Error('oh, no!'))

2. then() 方法

一个 promise 必须提供一个 then 方法,用来访问 promise 的当前 valuereason

  1. 它接收两个参数(都是可选的):

    • onFulfilled 方法表示状态从 pending -> fulfilled 时要执行的方法

    • onRejected 方法表示状态从 pending -> rejected 时要执行的方法

  2. 它必须返回一个 promise,为了实现 then() 的链式调用

 promise2 = promise1.then(onFulfilled, onRejected)

接下来,就按照规范的相关要求,一步一步实现自己的 Promise。

1. 支持构造函数和 then()

1.1 目标

第一版的目标,是支持以下用法:

// 需支持 new MyPromise()
let p = new MyPromise((resolve, reject) => {
    console.log('init MyPromise')
    resolve(1)
})
// 需支持 then() 方法
p.then(x => console.log(`x = ${x}`))

1.2 代码实现

function MyPromise(constructor) {

    this.state = 'pending'  // 当前状态
    this.value = undefined  // 状态从 pending -> fulfilled(成功)时的值
    this.reason = undefined // 状态从 pending -> rejected(失败)时的值

    const resolve = (value) => {
        // 状态从 pending -> fulfilled,不可逆
        if (this.state === 'pending') {
            this.state = 'fulfilled'
            this.value = value
        }
    }
    const reject = (reason) => {
        // 状态从 pending -> rejected,不可逆
        if (this.state === 'pending') {
            this.state = 'rejected'
            this.reason = reason
        }
    }

    // 捕获构造函数的异常,因为 constructor 是用户“自定义”传进来的
    try {
        constructor(resolve, reject)
    } catch (e) {
        reject(e)
    }
}

MyPromise.prototype.then = function (onFulfilled, onRejected) {
    // 根据不同的状态,执行对应的方法
    switch (this.state) {
        case 'fulfilled':
            // onFulfilled() 的第一个参数是 promise 的 value,且该方法只能调用一次
            onFulfilled(this.value)
            break;
        case 'rejected':
            // onRejected() 的第一个参数是 promise 的 reason,且该方法只能调用一次
            onRejected(this.reason)
            break;
    }
}

此时,运行目标代码,会成功输出:

init MyPromise
x = 1

另外,根据 Promises/A+ 规范的相关要求,还需要再完善下 then 方法,修改后的代码如下:

MyPromise.prototype.then = function (onFulfilled, onRejected) {
    //【标准 2.2.1】两个参数都是可选的,如果不是 function 则必须忽略
    // 在实践中,会给它们赋个默认值,用来实现 `then` 方法的值穿透
    //         也就是说,即便当前的 `then` 方法没传任何参数,promise 也能把值透传给下一个 `then` 方法 
    onFulfilled = (typeof onFulfilled === 'function') ? onFulfilled : function (x) { return x };
    onRejected = (typeof onRejected === 'function') ? onRejected : function (e) { throw e };

    //【标准 2.2.4】确保 onFulfilled() 和 onRejected() 异步执行,
    //             要在 `then` 所在的事件循环之后调用,得用个新堆栈
    // 在实践中,可以用宏任务机制(setTimeout, setImmediate)
    //          或微任务机制(MutationObserver, process.nextTick)
    // 考虑到 promise 本就属于微任务,所以更应该用“微任务”
    //          简单起见,这里就用 setTimeout 示意下
    switch (this.state) {
        case 'fulfilled':
            setTimeout(() => {
                onFulfilled(this.value)
            })
            break;
        case 'rejected':
            setTimeout(() => {
                onRejected(this.reason)
            })
            break;
    }
}

此时,运行目标代码,也可以正常输出。

1.3 实现思路

  1. 构造函数 constructor 由使用者自己提供

  2. promise 的状态迁移逻辑,由其内部来维护。但调用的时机,交给使用人员

    • 调用的时机,即 constructor() 回调函数的那两个参数

  3. then() 方法根据 promise 的内部状态,执行相应的回调函数

    • 这里的 switch-case-break 相当于 if, if

    • 如果没有 break,则执行完当前 case 后还会继续执行后面的 case

2. 支持异步 resolve 和 reject

2.1 目标

第二版的目标,是支持异步更新 promise 的状态。比如以下用法:

let p = new MyPromise((resolve, reject) => {
    console.log('init MyPromise')
    console.log('1 minute later...')

    // 模拟 ajax, fetch 等,异步更新 promise 的状态
    setTimeout(() => {
        resolve(1)
    }, 1000)
})
p.then(x => console.log(`x = ${x}`))

第一版的代码不支持上面的用法,如果用第一版的 MyPromise 执行,会在 1 秒后什么都不输出。因为在执行第 10 行的代码 p.then(...) 时,promise 的状态依然是 pending,所以就不会执行 then() 方法中的任何一个回调函数 onFulfilledonRejected

2.2 代码实现

要支持异步修改 promise 的状态,可以在状态是 pending 的时候就把 then() 方法的两个回调函数 onFulfilledonRejected 给存起来,然后等 promise 在真正发生状态迁移的时候——即在执行传给 constructor 函数的两个参数 resolve, reject 时,再执行它们。

改造后的代码,如下:

function MyPromise(constructor) {

    this.state = 'pending'
    this.value = undefined
    this.reason = undefined

    // 1. 新增两个属性,用来存储 then() 的两个回调
    //    之所以是数组,是因为同一个 promise 的 then() 方法可能会被调用多次【标准 2.2.6】
    this.onFulfilledArray = []
    this.onRejectedArray = []

    const resolve = (value) => {
        if (this.state === 'pending') {
            this.state = 'fulfilled'
            this.value = value

            // 3.1 待“异步”更新状态时,执行对应的回调函数
            this.onFulfilledArray.forEach((cb) => {
                cb(this.value)
            })
        }
    }
    const reject = (reason) => {
        if (this.state === 'pending') {
            this.state = 'rejected'
            this.reason = reason

            // 3.2 待“异步”更新状态时,执行对应的回调函数
            this.onRejectedArray.forEach((cb) => {
                cb(this.reason)
            })
        }
    }

    try {
        constructor(resolve, reject)
    } catch (e) {
        reject(e)
    }
}

MyPromise.prototype.then = function (onFulfilled, onRejected) {
    onFulfilled = (typeof onFulfilled === 'function') ? onFulfilled : function (x) { return x };
    onRejected = (typeof onRejected === 'function') ? onRejected : function (e) { throw e };

    switch (this.state) {
        // 2. 新增对 pending 状态的处理,先把回调函数和对应的值给存起来
        case 'pending':
            this.onFulfilledArray.push(() => {
                setTimeout(() => {
                    onFulfilled(this.value)
                })
            })
            this.onRejectedArray.push(() => {
                setTimeout(() => {
                    onRejected(this.reason)
                })
            })
            break;
        case 'fulfilled':
            setTimeout(() => {
                onFulfilled(this.value)
            })
            break;
        case 'rejected':
            setTimeout(() => {
                onRejected(this.reason)
            })
            break;
    }
}

此时,再执行上面的目标代码,就会在 1 秒后输出 x = 1

2.3 实现思路

  1. 用箭头函数或闭包,把不同状态的“回调函数和值”对应上,并存起来

  2. 基于观察者模式,等真正发生状态的改变时,再触发相应的回调函数

  3. 需注意:在处理 pending 状态时 setTimeout 的包裹范围

    • setTimeout 的初衷是为了确保回调参数 onFulfilled(), onRejected() 的异步执行

    • xxxArray.push() 是需要在当前 then 中执行的,所以在外面

3. 支持链式调用

3.1 目标

第三版的目标,是支持 then() 方法的链式调用,即 p.then().then().then()....then()

比如以下用法:

let p = new MyPromise((resolve, reject) => {
    console.log('init MyPromise')
    console.log('1 minute later...')

    setTimeout(() => {
        resolve(1)
    }, 1000)
})

// 需支持 then() 的链式调用
p.then(x => {
    console.log(`x = ${x}`)
    return 2
}).then(y => {
    console.log(`链式调用1, y = ${y}`)
}).then(z => {
    console.log(`链式调用2, z = ${z}`)
})

第二版的代码不支持 then() 方法的链式调用,如果执行上面的代码,会在第 14 行报错:

TypeError: Cannot read properties of undefined (reading 'then')

3.2 代码实现

为了支持 then() 的链式调用,就需要 then() 方法返回一个新的 promise,并把上一个 then() 的返回值传过去。同时,考虑到上个 then 执行的代码是用户“自定义”的回调函数,所以需要用 try-catch 包裹下。

修改 then 方法,代码如下:

MyPromise.prototype.then = function (onFulfilled, onRejected) {
    onFulfilled = (typeof onFulfilled === 'function') ? onFulfilled : function (x) { return x };
    onRejected = (typeof onRejected === 'function') ? onRejected : function (e) { throw e };

    // 1. 新增变量
    let promise2

    // 2.【标准 2.2.7】`then` must return a promise
    switch (this.state) {
        case 'pending':
            // 2.1 promise2 = new MyPromise((resolve, reject) => { ... })
            promise2 = new MyPromise((resolve, reject) => {
                this.onFulfilledArray.push(() => {
                    setTimeout(() => {
                        // 2.2 在 try-catch 块里 resolve 或 reject
                        try {
                            // 2.3 把上个 `then` 的执行结果传下去
                            let prev = onFulfilled(this.value)
                            resolve(prev)
                        } catch (e) {
                            reject(e)
                        }
                    })
                })
                this.onRejectedArray.push(() => {
                    setTimeout(() => {
                        try {
                            let prev = onRejected(this.reason)
                            resolve(prev)
                        } catch (e) {
                            reject(e)
                        }
                    })
                })
            })
            break;
        case 'fulfilled':
            promise2 = new MyPromise((resolve, reject) => {
                setTimeout(() => {
                    try {
                        let prev = onFulfilled(this.value)
                        resolve(prev)
                    } catch (e) {
                        reject(e)
                    }
                })
            })
            break;
        case 'rejected':
            promise2 = new MyPromise((resolve, reject) => {
                setTimeout(() => {
                    try {
                        let prev = onRejected(this.reason)
                        resolve(prev)
                    } catch (e) {
                        reject(e)
                    }
                })
            })
            break;
    }

    // 3. 返回新的 promise
    return promise2
}

此时,再执行目标代码,就会在 1 秒后输出:

x = 1
链式调用1, y = 2
链式调用2, z = undefined

3.3 实现思路

  • 构造一个新的 promise,同时用上一个 then() 的返回值来 resolve

  • 注意事项:

    • 在执行上一个 then 代码的时候,需要 try-catch

    • 在接收上一个 then 的执行结果时,只要不是异常,都会正常 resolve 给下个 then。比如:

Promise.reject(1).then(
    () => 2,
    () => 3,
).then((solution) => {
    // Fulfilled with 3
    console.log(`Resolved with ${solution}`)
})

4. 完善 then() 回调的返回值

4.1 目标

需要注意的是,在 promise 中,有两个返回值:

  1. then() 方法本身的返回值,需要返回一个新 promise,因为要实现 then() 的链式调用

  2. then() 方法的两个回调参数 onFulfilledonRejected,它们的返回值可以是原始值、对象或函数,甚至是另一个新 promise

实际上,这两个返回值是有关联的,那就是(本轮)onFulfilledonRejected 的返回值会决定(下一轮)then() 方法的返回值。

考虑下面的用法:

let p = new MyPromise((resolve, reject) => {
    console.log('init MyPromise')
    console.log('1 minute later...')

    setTimeout(() => {
        resolve(1) // 原始值 number
    }, 1000)
})

p.then(x => {
    console.log(`第1个then, x = ${x}`)
    // 返回 function
    return () => {
        console.log('第1个 then 返回 function')
    }
}).then(y => {
    console.log(`链式调用1, y = ${y}`)
    // 返回 object
    return { 'msg': '链式调用1 里返回 object' }
}).then(z => {
    console.log(`链式调用2, z = ${z}`)
    // 返回 promise(TODO)
    return new MyPromise(function (resolve, reject) {
        resolve('链式调用2 里返回 promise 的返回值')
    })
}).then(a => {
    console.log(`链式调用3, a = ${a}`)
    // 返回原始值 string
    return 'well done!'
}).then(a => {
    console.log(`链式调用4, a = ${a}`)
})

如果用第三版的代码,运行以上代码,会在 1 秒后输出如下内容。此时,第 27 行的输出并不符合预期。

1个then, x = 1
链式调用1, y = () => {
        console.log('第1个 then 返回 function')
    }
链式调用2, z = [object Object]
链式调用3, a = [object Object]
链式调用4, a = well done!

所以,第四版的目标,就是让 onFulfilledonRejected 回调函数的返回值支持 promise。

4.2 代码实现

要完善 onFulfilledonRejected 回调函数的返回值,就需要重新定义下 then() 方法中对 promise2 的 resolve 逻辑。

新增 resolvePromise 函数,代码如下:

/**
 * 【标准 2.3】The Promise Resolution Procedure
 *
 * @param {*} promise 当前 `then` 方法的 promise 值
 * @param {*} x       上一个 `then` 方法的执行结果,即当前 `then` 方法的回调函数 `onFulfilled` 或 `onRejected` 的返回值
 * @param {*} resolve 当前 `then` 方法要返回的 promise2 的 resolve
 * @param {*} reject  当前 `then` 方法要返回的 promise2 的 reject
 */
function resolvePromise(promise, x, resolve, reject) {
    //【标准 2.3.1】x 和 promise 不能指向同一个对象
    if (x === promise) {
        return reject(new TypeError('Cyclic reference'))
    }

    //【标准 2.3.3】x 是一个对象或函数
    if ((typeof x === 'object' && x !== null) || (typeof x === 'function')) {

        //【标准 2.3.3.3】若多次调用,则只执行第一次的
        let thenHasClalled = false

        try {
            //【标准 2.3.3.1】先存起来,避免多次读取(一次判断类型,一次调用执行)产生副作用
            let then = x.then

            //【标准 2.3.3.3】x 是 promise, thenable
            if (typeof then === 'function') {
                then.call(x, (y) => {
                    if (thenHasClalled) return
                    thenHasClalled = true
                    return resolvePromise(promise, y, resolve, reject)
                }, (e) => {
                    if (thenHasClalled) return
                    thenHasClalled = true
                    return reject(e)
                })
            } else {
                // x 是普通的对象或函数
                return resolve(x)
            }
        } catch (e) {
            if (thenHasClalled) return
            thenHasClalled = true
            return reject(e)
        }
    } else {
        //【标准 2.3.4】x 是原始值
        return resolve(x)
    }
}

同时,替换 then 方法里处理 prev 变量的 resolve(prev) 方法,如下:

MyPromise.prototype.then = function (onFulfilled, onRejected) {
    ...
    // resolve(prev)
    resolvePromise(promise2, prev, resolve, reject)
    ...
}

此时,再执行目标代码,就会在 1 秒后输出:

1个then, x = 1
链式调用1, y = () => {
        console.log('第1个 then 返回 function')
    }
链式调用2, z = [object Object]
链式调用3, a = 链式调用2 里返回 promise 的返回值
链式调用4, a = well done!

4.3 相关说明

关于 resolvePromise() 函数的说明:

  • if (x instance of MyPromise) 的逻辑,合并到了 if (typeof then === 'function')

  • return 语句,这里的主要作用是跳出函数结束执行,因为并没有地方用到它的返回值

5. 最终代码

5.1 prototype 初版

// 参考 ECMAScript 规范,改了两个变量名:
// 1. MyPromise() 的参数 constructor -> executor
// 2. state -> status
function MyPromise(executor) {

    this.status = 'pending'  // 当前状态
    this.value = undefined  // 状态从 pending -> fulfilled(成功)时的值
    this.reason = undefined // 状态从 pending -> rejected(失败)时的值

    // 这两个属性,用来存储 then() 的两个回调
    // 之所以是数组,是因为同一个 promise 的 then() 方法可能会被调用多次【标准 2.2.6】
    this.onFulfilledArray = []
    this.onRejectedArray = []

    const resolve = (value) => {
        // 状态从 pending -> fulfilled,不可逆
        if (this.status === 'pending') {
            this.status = 'fulfilled'
            this.value = value

            // 待“异步”更新状态时,执行对应的回调函数
            this.onFulfilledArray.forEach((cb) => {
                cb(this.value)
            })
        }
    }
    const reject = (reason) => {
        // 状态从 pending -> rejected,不可逆
        if (this.status === 'pending') {
            this.status = 'rejected'
            this.reason = reason

            // 待“异步”更新状态时,执行对应的回调函数
            this.onRejectedArray.forEach((cb) => {
                cb(this.reason)
            })
        }
    }

    // 捕获构造函数的异常,因为 executor 是用户“自定义”传进来的
    try {
        executor(resolve, reject)
    } catch (e) {
        reject(e)
    }
}


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

    //【标准 2.2.1】两个参数都是可选的,如果不是 function 则必须忽略
    // 在实践中,会给它们赋个默认值,用来实现 `then` 方法的值穿透
    //         也就是说,即便当前的 `then` 方法没传任何参数,promise 也能把值透传给下一个 `then` 方法 
    onFulfilled = (typeof onFulfilled === 'function') ? onFulfilled : (x) => x;
    onRejected = (typeof onRejected === 'function') ? onRejected : (e) => { throw e };

    //【标准 2.2.7】`then` must return a promise
    let promise2

    //【标准 2.2.4】确保 onFulfilled() 和 onRejected() 异步执行,
    //             要在 `then` 所在的事件循环之后调用,得用个新堆栈
    // 在实践中,可以用宏任务机制(setTimeout, setImmediate)
    //          或微任务机制(MutationObserver, process.nextTick)
    // 考虑到 promise 本就属于微任务,所以更应该用“微任务”
    //          简单起见,这里就用 setTimeout 示意下
    switch (this.status) {
        case 'pending':
            promise2 = new MyPromise((resolve, reject) => {
                this.onFulfilledArray.push(() => {
                    setTimeout(() => {
                        try {
                            let prev = onFulfilled(this.value)
                            // resolve(prev)
                            resolvePromise(promise2, prev, resolve, reject)
                        } catch (e) {
                            reject(e)
                        }
                    })
                })
                this.onRejectedArray.push(() => {
                    setTimeout(() => {
                        try {
                            let prev = onRejected(this.reason)
                            // resolve(prev)
                            resolvePromise(promise2, prev, resolve, reject)
                        } catch (e) {
                            reject(e)
                        }
                    })
                })
            })
            break;
        case 'fulfilled':
            promise2 = new MyPromise((resolve, reject) => {
                setTimeout(() => {
                    try {
                        let prev = onFulfilled(this.value)
                        // resolve(prev)
                        resolvePromise(promise2, prev, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                })
            })
            break;
        case 'rejected':
            promise2 = new MyPromise((resolve, reject) => {
                setTimeout(() => {
                    try {
                        let prev = onRejected(this.reason)
                        // resolve(prev)
                        resolvePromise(promise2, prev, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                })
            })
            break;
    }

    // 返回新的 promise
    return promise2
}


/**
 * 【标准 2.3】The Promise Resolution Procedure
 *
 * @param {*} promise 当前 `then` 方法的 promise 值
 * @param {*} x       上一个 `then` 方法的执行结果,即当前 `then` 方法的回调函数 `onFulfilled` 或 `onRejected` 的返回值
 * @param {*} resolve 当前 `then` 方法要返回的 promise2 的 resolve
 * @param {*} reject  当前 `then` 方法要返回的 promise2 的 reject
 */
function resolvePromise(promise, x, resolve, reject) {
    //【标准 2.3.1】x 和 promise 不能指向同一个对象
    if (x === promise) {
        return reject(new TypeError('Cyclic reference'))
    }

    //【标准 2.3.3】x 是一个对象或函数
    if ((typeof x === 'object' && x !== null) || (typeof x === 'function')) {

        //【标准 2.3.3.3】若多次调用,则只执行第一次的
        let thenHasClalled = false

        try {
            //【标准 2.3.3.1】先存起来,避免多次读取(一次判断类型,一次调用执行)产生副作用
            let then = x.then

            //【标准 2.3.3.3】x 是 promise, thenable
            if (typeof then === 'function') {
                then.call(x, (y) => {
                    if (thenHasClalled) return
                    thenHasClalled = true
                    return resolvePromise(promise, y, resolve, reject)
                }, (e) => {
                    if (thenHasClalled) return
                    thenHasClalled = true
                    return reject(e)
                })
            } else {
                // x 是普通的对象或函数
                return resolve(x)
            }
        } catch (e) {
            if (thenHasClalled) return
            thenHasClalled = true
            return reject(e)
        }
    } else {
        //【标准 2.3.4】x 是原始值
        return resolve(x)
    }
}

// ES6 Module
// export default MyPromise

5.2 prototype 完善微任务

在实现 MyPromise.prototype.then 方法时,为了确保 onFulfilled/onRejected 回调函数的异步执行,用了宏任务 setTimeout。但这会带来延迟问题,因为两次 Event Loop 之间有时间间隔,其中浏览器约 4ms,Node.js 约 1ms。

所以,还是得用“微任务”机制,宗旨就是:在确保异步执行的同时,“尽早”地调用所有已经加入队列的回调函数。

5.3 prototype 完善封装性

5.4 class 版

6. 测试

使用 promises-aplus-tests 对最终的代码实现进行测试,看它是否符合 Promise/A+ 的规范。

6.1 新增代码

根据文档说明,修改 MyPromise.js 文件,新增以下代码:

// promises-aplus-tests 的测试需要
MyPromise.resolve = function (value) {
    return new MyPromise((resolve, reject) => {
        resolve(value)
    })
}
MyPromise.reject = function (reason) {
    return new MyPromise((resolve, reject) => {
        reject(reason)
    })
}
MyPromise.deferred = function () {
    let dfd = {}
    dfd.promise = new MyPromise((resolve, reject) => {
        dfd.resolve = resolve
        dfd.reject = reject
    })
    return dfd
}

// CommonJS,在 Node.js 环境中用
module.exports = MyPromise

6.2 开始测试

6.2.1 本地安装

npm init --yes
npm install promises-aplus-tests --save-dev  # 本地安装
npm run test                                 # 执行脚本,开始测试

package.json 文件的相关配置,如下:

{
  "scripts": {
    "test": "promises-aplus-tests MyPromise.js"
  },
  "devDependencies": {
    "promises-aplus-tests": "^2.1.2"
  }
}

6.2.2 npx

如果不想 npm install,也可以用 npx 直接运行,命令如下:

npx promises-aplus-tests MyPromise.js  # 执行脚本,开始测试

6.3 测试结果

872 个测试用例都成功通过。至此,我们就实现了一个符合 Promises/A+ 规范的 then() 方法。

7. 主要参考

Last updated