完全符合Promise/A+规范的Promise源码实现

570

前言

三年多以前,抱着学习的态度,写一篇Promise实现原理的文章: Promise实现原理(附源码),文章了里面体现了Promise的思想和基本原理,对于当时的自己来说确实足够了。后来随着自己的学习和积累,再回过头有来看那篇文章,发现里面欠考虑的东西还是比较多的,实现的代码不符合Promise/A+规范,所以又写了这篇。

相比于前一篇,这篇文章:

  • 完全符合Promise/A+规范,通过了872个case
  • 新增ECMA中Promise的新方法
  • 只给出了实现的代码和部分注释,没有讲解过程

代码实现

const isFunction = function(fun) {
    return typeof fun === 'function';
};
const isObject = function(value) {
    return value !== null && typeof value === 'object';
};

// [[Resolve]](promise, x): 是promise的解决过程,判断x的值的类型以及它和promise实例的关系
// 需要进行 [[Resolve]](promise, x)的地方有:
// 1. then中的onFulfilled和onRejected的返回值res:[[Resolve]](promise, res)
// 2. _promiseResolve中,x为Promise的情况,对改promise的值y进行[[Resolve]](promise, y)
// 3. _promiseResolve中,x有then且then为函数的情况,该then的第一个参数是函数resolvePromise接收一个y为参数,对y进行[[Resolve]](promise, y)
class MyPromise {
    constructor(handle) {
        if (!isFunction(handle)) {
            throw new TypeError('MyPromise resolver is not a function');
        }
        // 当前promise的状态
        this._status = 'PENDING';
        // 当前promise的值
        this._value = undefined;
        // promise状态为'FULFILLED'时需要执行的注册事件队列
        this._fulfilledQueue = [];
        // promise状态为'REJECTED'时需要执行的注册事件队列
        this._rejectedQueue = [];
        // promise创建后,同步执行handle
        // 如果handle执行出错,则将promise的状态改为'REJECTED'
        try {
            handle(this._resolve.bind(this), this._reject.bind(this));
        } catch(err) {
            this._reject(err);
        }
    }
    _resolve(value) {
        if (this._status !== 'PENDING') {
            return;
        }
        const resolveHandle = (val) => {
            this._status = 'FULFILLED';
            this._value = val;
            while(this._fulfilledQueue.length) {
                const cb = this._fulfilledQueue.shift();
                cb(val);
            }
        }
        const rejectHandle = (reason) => {
            this._status = 'REJECTED';
            this._value = reason;
            while(this._rejectedQueue.length) {
                const cb = this._rejectedQueue.shift();
                cb(reason);
            }
        }
        // 判断resolve传入的是不是一个promise
        // 如果是,需要等待该promise的状态改变,新promise的状态和该状态相同
        // 注意:Promise/A+规范并没有这个规定
        if (value instanceof MyPromise) {
            if (value === this) {
                return rejectHandle(new TypeError(''));
            }
            value.then(resolveHandle, rejectHandle);
        } else {
            // 执行_fulfilledQueue中的任务
            // 所有 onFulfilled 需按照其注册顺序依次回调
            resolveHandle(value);
        }
    }
    _reject(reason) {
        if (this._status !== 'PENDING') {
            return;
        }
        this._status = 'REJECTED';
        this._value = reason;
        // 执行_rejectedQueue中的任务
        // 所有的 onRejected 需按照其注册顺序依次回调
        while(this._rejectedQueue.length) {
            const cb = this._rejectedQueue.shift();
            cb(reason);
        }
    }
    // Promise解决过程
    // [[Resolve]](promise, x)
    _promiseResolve(promise, x, resolve, reject) {
        // x与promise相等
        // 如果promise和x指向同一对象,以TypeError为reason执行reject
        if (x === promise) {
            return reject(new TypeError(''));
        }
        // x为Promise
        // 如果 x 为 Promise ,则使 promise 接受 x 的状态
        if (x instanceof MyPromise) {
            // x的value为y, 运行[[Resolve]](promise, y):this._promiseResolve(promise, y, resolve, reject);
            // 有可能是一个promise,所以需要调用解决过程
            x.then(
                y => {
                    this._promiseResolve(promise, y, resolve, reject);
                },
                reject
            );
            return;
        }
        // x为对象或函数
        if (isFunction(x) || isObject(x)) {
            let isCalled  = false;
            let then;
            // 如果取 x.then 的值时抛出错误 e ,则以e为reason执行reject(e)
            try {
                then = x.then;
            } catch(e) {
                return reject(e);
            }
          
            // 如果 then 是函数,将 x 作为函数的作用域 this 调用之。传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做 rejectPromise:
            // 如果 resolvePromise 以 y 为参数被调用,则运行 [[Resolve]](promise, y): this._promiseResolve(promise, y, resolve, reject);
            // 如果 rejectPromise 以 r 为参数被调用,则执行reject(r);
            // 如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
            if (isFunction(then)) {
                const resolvePromise = y => {
                    if (isCalled) {
                        return;
                    }
                    isCalled = true;
                    this._promiseResolve(promise, y, resolve, reject);
                }
                const rejectPromise = (r) => {
                    if (isCalled) {
                        return;
                    }
                    isCalled = true;
                    reject(r);
                }
                // 如果调用 then 方法抛出了异常 e
                // 如果 resolvePromise 或 rejectPromise 已经被调用,则忽略之
                // 否则以 e 为reason执行reject(e);
                try {
                    then.call(x, resolvePromise, rejectPromise);
                } catch(e) {
                    if (isCalled) {
                        return;
                    }
                    reject(e);
                }
            // 如果 then 不是函数(包括没有then的情况),以 x 为参数执行resolve(x);
            } else {
                resolve(x);
            }
            return;
        }
        // 如果 x 不为对象或者函数,以 x 为参数执行resolve(x);
        resolve(x);
    }
    then(onFulfilled, onRejected) {
        const promise = new MyPromise((resolveNext, rejectedNext) => {
            const fulfilledHandle = (value) => {
                queueMicrotask(() => {
                    if (!isFunction(onFulfilled)) {
                        resolveNext(value);
                    } else {
                        try {
                            const res = onFulfilled(value);
                            // 执行promise的解决过程
                            this._promiseResolve(promise, res, resolveNext, rejectedNext);
                        } catch(err) {
                            rejectedNext(err);
                        }
                    }
                });
                
            };
            const rejectedHandle = (reason) => {
                queueMicrotask(() => {
                    if (!isFunction(onRejected)) {
                        rejectedNext(reason);
                    } else {
                        try {
                            const res = onRejected(reason);
                            // 执行promise的解决过程
                            this._promiseResolve(promise, res, resolveNext, rejectedNext);
                        } catch(err) {
                            rejectedNext(err);
                        }
                    }
                });
            };

            switch(this._status) {
                // 状态为"PENDING'时,将任务加入到队列中等待promise的状态改变时执行
                case 'PENDING':
                    this._fulfilledQueue.push(fulfilledHandle);
                    this._rejectedQueue.push(rejectedHandle);
                    break;
                case 'FULFILLED':
                    fulfilledHandle(this._value);
                    break;
                case 'REJECTED':
                    rejectedHandle(this._value);
                    break;
                default:
            }
        });
        return promise;
    }
    catch(onRejected) {
        return this.then(undefined, onRejected);
    }
    // finally返回的新promise的状态取决于调用finally的promise的状态
    // finally方法总是会返回原来的值
    // 如果cb返回的是一个promise, 会等待该promise的状态改变
    finally(cb) {
        return this.then(
            (value) => MyPromise.resolve(cb()).then(() => value),
            (err) => MyPromise.resolve(cb()).then(() => { throw err })
        );
    }
    static resolve(p) {
        if (p instanceof MyPromise) {
            return p;
        }
        return new MyPromise((resolve) => {
            resolve(p);
        });
    }
    // MyPromise.reject()方法的参数,会原封不动地作为reject的理由, 不会处理传入promise的情况
    static reject(p) {
        return new MyPromise((resolve, reject) => {
            reject(p);
        });
    }
    // 如果不是传入的数组某一项不是MyPromise实例,就会先调用MyPromise.resolve()方法,将参数转为MyPromise实例,再进一步处理
    static all(promiseList) {
        return new MyPromise((resolve, reject) => {
            let list;
            // 如果promiseList不是iterator,会报错,直接reject
            try {
                list = [...promiseList];   
            } catch(err) {
                reject(new TypeError(`${promiseList} is not iterable (cannot read property Symbol(Symbol.iterator))`));
                return;
            }

            const len = list.length;
            // promiseList为空数组,resolve
            if (len === 0) {
                 resolve([]);
            }
            
            let fulfilledCount = 0;
            const res = [];
            for (const [i, p] of list.entries()) {
                MyPromise.resolve(p)
                    .then((value) => {
                        res[i] = value;
                        fulfilledCount++;
                        if (fulfilledCount === len) {
                            resolve(res);
                        }
                    }, (err) => {
                        reject(err);
                    });
            }
        });
    }
    // 如果不是传入的数组某一项不是MyPromise实例,就会先调用MyPromise.resolve()方法,将参数转为MyPromise实例,再进一步处理
    static race(promiseList) {
        return new MyPromise((resolve, reject) => {
            let list;
            // 如果promiseList不是iterator,会报错,直接reject
            try {
                list = [...promiseList];   
            } catch(err) {
                reject(new TypeError(`${promiseList} is not iterable (cannot read property Symbol(Symbol.iterator))`));
                return;
            }
            // 会等待第一个改变状态的promise,如果promiseList为空数组,则会一直是pending状态
            for (const p of list) {
                MyPromise.resolve(p)
                    .then((value) => {
                        resolve(value);
                    }, (err) => {
                        reject(err);
                    });
            }
        });
    }
    // MyPromise.allSettled()方法接受一组Promise实例作为参数,f返回一个新的Promise实例,且新实例的状态只可能变成fulfilled
    // 只有等到所有这些参数实例都返回结果,不管是fulfilled还是rejected,包装实例才会结束。该方法由 ES2020 引入。
    /** 返回新的promise的结果
     * [
        { status: 'fulfilled', value: 42 },
        { status: 'rejected', reason: -1 }
        ]
     */
    static allSettled(promiseList) {
        return new MyPromise((resolve, reject) => {
            let list;
            // 如果promiseList不是iterator,会报错,直接reject
            try {
                list = [...promiseList];   
            } catch(err) {
                reject(new TypeError(`${promiseList} is not iterable (cannot read property Symbol(Symbol.iterator))`));
                return;
            }

            let unsettledCount = list.length;
            // promiseList为空数组,resolve
            if (unsettledCount === 0) {
                 resolve([]);
            }
            
            const res = [];
            for (const [i, p] of list.entries()) {
                MyPromise.resolve(p)
                    .then(value => {
                        res[i] = {status: 'fulfilled', value};
                    }, reason => {
                        res[i] = {status: 'rejected', reason};
                    }).finally(() => {
                        unsettledCount--;
                        if (unsettledCount === 0) {
                            resolve(res);
                        }
                    });
            }
        });
    }
    // 与Promise.all相反,如果有一个为fulfilled,返回的新promise就是fulfilled,否则就是rejected
    // 数组为空或者所有promise都是rejected状态,返回一个AggregateError:new AggregateError(errors, 'All promises were rejected')
    // errors数组保存每个失败的原因,如果数组为空,则errors为空
    static any(promiseList) {
        return new MyPromise((resolve, reject) => {
            let list;
            // 如果promiseList不是iterator,会报错,直接reject
            try {
                list = [...promiseList];   
            } catch(err) {
                reject(new TypeError(`${promiseList} is not iterable (cannot read property Symbol(Symbol.iterator))`));
                return;
            }

            const len = list.length;
            const errors = [];
            // promiseList为空数组,表示没有一个promise会是fulfilled状态,所以结果为rejected
            if (len === 0) {
                reject(new AggregateError(errors, 'All promises were rejected'));
            }
            let rejectedCount = 0;

            for (const [i, p] of list.entries()) {
                MyPromise.resolve(p)
                    .then(
                        value => {
                            resolve(value);
                        },
                        err => {
                            errors[i] = err;
                            rejectedCount++;
                            if (rejectedCount === len) {
                               reject(new AggregateError(errors, 'All promises were rejected'));
                            }
                        }
                    );
            }
        });
    }
}

// 测试代码
// MyPromise.deferred = function() {
//     var result = {};
//     result.promise = new MyPromise(function(resolve, reject){
//       result.resolve = resolve;
//       result.reject = reject;
//     });
  
//     return result;
// }


// var promisesAplusTests = require("promises-aplus-tests");
// const { reject } = require("underscore");

// promisesAplusTests(MyPromise, function (err) {
//     console.log(err);
//     // All done; output is in the console. Or check `err` for number of failures.
// });