实现Promise 或者是all、race、any等

问题1:

Promise/A+ 约定了哪些规范?

问题2:

手动实现 Promise 的过程中都遇到过哪些问题?

Promise/A+ 规范

术语

  • promise:是一个具有then 方法的对象或者函数,它的行为符合该规范
  • thenable:是一个定义了then 方法的对象或函数
  • value:可以是任何一个合法的 JavaScript 的值(包括undefined、thenable 或 promise)
  • exception:是一个异常,是在 Promise 里面可以用 throw 语句抛出来的值
  • reason:是一个 Promise 里 reject 之后返回的拒绝原因

状态描述

一个 Promise 有三种状态:pendingfulfilledrejected

当状态为 pending 状态时,即可以转换为 fulfilled 或者 rejected 其中之一

当状态为 fulfilled 状态时,就不能转换为其他状态了,必须返回一个不能再改变的值

当状态为 rejected 状态时,同样也不能转换为其他状态,必须有一个原因的值也不能改变

then 方法

一个 Promise 必须拥有一个 then 方法来访问它的值或者拒绝原因

promise.then(onFulfilled, onRejected)

onFulfilled 和 onRejected 特性

如果 onFulfilled 是函数,则当 Promise 执行结束之后必须被调用,最终返回值为 value,其调用次数不可超过一次

而 onRejected 除了最后返回的是 reason 外,其他方面和 onFulfilled 在规范上的表述基本一样

多次调用

then 方法其实可以被一个 Promise 调用多次且必须返回一个 Promise 对象

promise2 = promise1.then(onFulfilled, onRejected);

实现一个符合 Promise/A+ 规范的 Promise

构造函数

Promise 构造函数接受一个 executor 函数

executor 函数执行完同步或异步操作后,调用它的两个参数 resolve 和 reject

try {
    module.exports = Promise;
}
function Promise(executor) {
    var self = this
    self.status = 'pending' // Promise 当前的状态
    self.data = undefined // Promise 的值
    self.onResolvedCallback = [] // Promise resolve时的回调函数集
    self.onRejectedCallback = [] // Promise reject时的回调函数集
    function resolve(value) {
        if (value instanceof Promise) {
            return value.then(resolve, reject);
        }
    }
    setTimeout(function() { // 异步执行所有的回调函数
        if (self.status === 'pending') {
            self.status = 'resolved'
            self.data = value
            for (var i = 0; i < self.onResolvedCallback.length; i++) {
                self.onResolvedCallback[i](value);
            }
        }
    })
    function reject(reason) {
        setTimeout(function() { // 异步执行所有的回调函数
            if (self.status === 'pending') {
                self.status = 'rejected'
                self.data = reason
                for (var i = 0; i < self.onRejectedCallback.length; i++) {
                    self.onRejectedCallback[i](reason);
                }
            }
        })   
    }
    try { // 考虑到执行过程中有可能出错,所以我们用try/catch 块给包起
        executor(resolve, reject) // 执行 executor
    } catch(e) {
        reject(e)
    }
}

function resolvePromise(promise2, x, resolve, reject) {
    var then
    var thenCalledOrThrow = false
    if (promise2 === x) {
        return reject(new TypeError('Chaning cycle detected for promise!'))
    }
    if (x instanceof Promise) {
        if (x.status === 'pending') {
            x.then(function(v) {
                resolvePromise(promise2, v, resolve, reject)
            }, reject)
        } else {
            x.then(resolve, reject)
        }
        return
    }
    if ((x !== null) && ((typeof x === 'object') || (typeof x === 'function'))) {
        try {
            then = x.then
            if (typeof then === 'function') {
                then.call(x, function rs(y) {
                    if (thenCalledOrThrow) return
                    thenCalledOrThrow = true
                    return resolvePromise(promise2, y, resolve, reject)
                }, function rj(r) {
                    if (thenCalledOrThrow) return
                    thenCalledOrThrow = true
                    return reject(r)
                })
            } else {
                resolve(x)
            }
        } catch (e) {
            if (thenCalledOrThrow) return
            thenCalledOrThrow = true
            return reject(e)
        }
    } else {
        resolve(x)
    }
}

实现 then 方法

then 方法是 Promise 执行完之后可以拿到 value 或者 reason 的方法并且还要保持then 执行之后

返回的依旧是一个 Promise 方法,还要支持多次调用

// then 方法接收两个参数 onResolved 和 onRejected,分别为 Promise 成功或失败后的回调
Promise.prototype.then = function(onResolved, onRejected) {
    var self = this;
    var promise2
    // 根据标准,如果then 的参数不是function,则需要忽略它
    onResolved = typeof onResolved === 'function' ? onResolved : function(v) { return v }
    onRejected = typeof onRejected === 'function' ? onRejected : function(r) { return r }
    if (self.status === 'resolved') {
        // 如果 promise1 的状态已经确定并且是 resolved,我们调用 onResolved,考虑到有可能 throw,所以还需要将其包在try/catch块里
        return promise2 = new Promise(function(resolve, reject) {
            setTimeout(function() { // 异步执行 onResolved
                try {
                    var x = onResolved(self, data)
                    resolvePromise(promise2, x, resolve, reject)
                } catch (reason) {
                    reject(reason)
                }
            })
            // try {
            //     var x = onResolved(self, data)
            //     if (x instanceof Promise) {
            //         // 如果 onResolved 的返回值是一个 Promise 对象,直接取它的结果作为 Promise2 的结果
            //         x.then(resolve, reject)
            //     }
            //     resolve(x) // 否则,以它的返回值作为 promise2 的结果
            // } catch (e) {
            //     reject(e); // 如果出错,以捕获到的错误作为promise2 的结果
            // }
        })
    }
    // 此处与前一个 if 块的逻辑几乎相同,区别在于所调用的是 onRejected 函数
    if (self.status === 'rejected') {
        return promise2 = new Promise(function(resolve, reject) {
            setTimeout(function() { // 异步执行 onRejected
                try {
                    var x = onRejected(self.data)
                    resolvePromise(promise2, x, resolve, reject)
                } catch (reason) {
                    reject(reason)
                }
            })
            // try {
            //     var x = onRejected(self.data)
            //     if (x instanceof Promise) {
            //         x.then(resolve, reject);
            //     }
            // } catch (e) {
            //     reject(e);
            // }
        })
    }
    if (self.status === 'pending') {
        // 如果当前的 Promise 还处于 pending 状态,我们并不是确定调用 onResolved 还是 onRejected,只能等到 Promise 的状态确定后,才能确定如何处理
        return promise2 = new Promise(function(resolve, reject) {
            self.onResolvedCallback.push(function(value) {
                try {
                    var x = onResolved(self.data)
                    resolvePromise(promise2, x, resolve, reject)
                    // if (x instanceof Promise) {
                    //     x.then(resolve, reject)
                    // }
                } catch (r) {
                    reject(r)
                }
            })
            self.onRejectedCallback.push(function(reason) {
                try {
                    var x = onRejected(self.data)
                    resolvePromise(promise2, x, resolve, reject)
                    // if (x instanceof Promise) {
                    //     x.then(resolve, reject)
                    // }
                } catch (r) {
                    reject(re)
                }
            })
        })
    }
}
Promise.prototype.catch = function (onRejected) {
    return this.then(null, onRejected)
}

// 测试
Promise.deferred = Promise.defer = function() {
    var dfd = {}
    dfd.promise = new Promise(function(resolve, reject) {
        dfd.resolve = resolve
        dfd.reject = reject
    })
    return dfd
}

Promise 并不是 JS 一开始存在的标准,如果你使用的某一个库中封装了一个 Promise 的实现,想象一下如果它不能跟你自己使用的 Promise 实现交互的情况,其实还是会有问题的