根据Promise/A+ 规范实现Promise

202 阅读5分钟

一、 constructor实现

根据规范2.1,一个Promise有三种状态(pending,fulfilled,rejected),所以首先定义三个状态常量:

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

接下来实现构造器MyPromise:

function MyPromise(executor) {
    // 只能作为构造函数调用
    if (new.target !== MyPromise) {
        throw Error('MyPromise must be called as a constructor');
    }
    //executor必须为函数
    if (typeof executor !== 'function') {
        throw TypeError('executor must be a function');
    }
    //初始化状态为pending
    this.state = PENDING;
    this.value = null;
    this.reason = null;
    //存放状态变为fulfilled时需执行的回调函数
    this.onFulfilledCallbacks = [];
    //存放状态变为rejected时需执行的回调函数
    this.onRejectedCallbacks = [];
    
    this.resolve = (value) => {} //稍后实现
    this.reject = (err) => {} // 稍后实现
    
    try {
        //构造函数执行时立即执行executor函数,接收resolve和reject两个参数
        executor(this.resolve, this.reject);
    }catch(e){
        // 当executor执行发生错误时,调用this.reject修改MyPromise的状态,并执行状态为rejected时的回调
        this.reject(e);
    }
}

二、 resolve函数实现

this.resolve = (value) => {
    if (value instanceof MyPromise) {
        // 当value状态改变时接收终值
        return void value.then(this.resolve, this.reject);
    }
    // 规范2.2.4 onFulfilled 和 onRejected 只有在执行环境堆栈仅包含平台代码时才可被调用,
    // 规范注解1,确保onFulfilled异步调用
    setTimeout(() => {
    // 规范2.1.2.1 当状态由pending变为fulfilled时,不得再改变,因此加这个判断
        if (this.state === PENDING) {
            this.value = value;
            // 修改状态为fulfilled
            this.state = FULFILLED;
            //规范 2.2.6.1 当 promise 成功执行时,所有 onFulfilled 需按照其注册顺序依次回调
            // 执行所有onFulfilled回调
            this.onFulfilledCallbacks.forEach(cb => cb());
    }, 0);
}

三、reject函数实现

this.reject = (err) => {
    setTimeout(() => {
        // 规范2.1.2.1 当状态由pending变为fulfilled时,不得再改变,因此加这个判断
        if (this.state === PENDING) {
            this.reason = err;
            // 修改状态为 rejected
            this.state = REJECTED;
            //规范 2.2.6.2 当 promise 成功执行时,所有 onFulfilled 需按照其注册顺序依次回调
            // 执行所有onRejected回调
            this.onRejectedCallbacks.forEach(cb => cb());
    }, 0);
}

四、 then方法实现

MyPromise.prototype.then = function(onFulfilled, onRejected) {
    let promise;
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v;
    onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err; };
    
    if (this.state === FULFILLED) {
        promise = new Promise((resolve, reject) => {
            // 规范2.2.4 onFulfilled 和 onRejected 只有在执行环境堆栈仅包含平台代码时才可被调用,
            // 规范注解1,确保onFulfilled异步调用
            setTimeout(() => {
                try {
                    // 规范2.2.5 onFulfilled和onRejected必须作为函数调用
                    const x = onFulfilled(this.value);
                    //规范2.2.7.1 如果 onFulfilled 或者 onRejected 返回一个值 x ,
                    //则运行Promise 解决过程:[[Resolve]](promise2, x)
                    resolutionProcedure(promise, x); //稍后实现
                }catch(e) {
                    //规范2.2.7.2 如果 onFulfilled 或者 onRejected 抛出一个异常 e ,则 promise2 必须拒绝执行,并返回拒因 e
                    reject(e)
                }
            }, 0)
        })
    }
    if (this.state === REJECTED) {
        promise = new MyPromise((resolve, reject) => {
            setTimeout(() => {
                try {
                    const x = onRejected(this.reason);
                    resolutionProcedure(promise, x); 
                }catch(e) {
                    reject(e);
                }
            }, 0)
        })
    }
    if (this.state = PENDING) {
        promise = new MyPromise((resolve, reject) => {
            // 当状态为pending时,将onFulfilled和onRejected回调收集起来
            this.onFulfilledCallbacks.push(() => {
                try {
                    const x = onFulfilled(this.value);
                    resolutionProcedure(promise, x);
                }catch(e) {
                    reject(e);
                }
            });
            this.onRejectedCallbacks.push(() => {
                try {
                    const x = onReject(this.reason);
                    resolutionProcedure(promise, x);
                }catch (e) {
                    reject(e);
                }
            })
        })
    }
    // 规范2.2.7 then方法必须返回一个新的promise对象
    return promise;
}

上面的函数有一些重复代码,我们可以提取出一个公共函数modifyPromise2(promise2, value, cb, reject),其中promise2为then方法要返回的新的promise,value为状态为fulfilled或rejected时对应的value/reason,cb为onFulfilled或onRejected回调函数

function modifyPromise2(promise2, value, cb, reject) {
    try {
        const x = cb(value);
        resolutionProcedure(promise2, x);
    }catch(e) {
        reject(e);
    }
}

因此,最终then方法为:

MyPromise.prototype.then = function(onFulfilled, onRejected) {
    let promise;
    //规范 2.2.1.1 忽略不是函数的onFulfilled,使用identity函数使值能够传递给下个then调用
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v;
    //规范 2.2.1.2 忽略不是函数的onRejected,重置为一个将传入参数作为错误抛出的函数,使错误能够
    //传递给下一个then调用
    onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err; };
    
    if (this.state === FULFILLED) {
        promise = new MyPromise((resolve, reject) => {
            setTimeout(() => {
                modifyPromise2(promise, this.value, onFulfilled, reject);
            });
        });
    }
    if (this.state === REJECTED) {
        promise = new MyPromise((resolve, reject) => {
            setTimeout(() => {
                modifyPromise2(promise, this.reason, onRejected, reject);
            })
        })
    }
    
    if (this.state === PENDING) {
        promise = new MyPromise((resolve, reject) => {
            this.onFulfilledCallbacks.push(() => {
                modifyPromise2(promise, this.value, onFulfilled, reject);
            });
            this.onRejectedCallbacks.push(() => {
                modifyPromise2(promise, this.reason, onRejected, reject);
            })
        })
    }
    return promise;
}

五、 resolutionProcedure(promise, x)实现

function resolutionProcedure(promise, x) {
    // 规范2.3.1 如果 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promise
    if (promise === x) {
        throw TypeError('循环引用,会引起无穷递归');
    }
    
    if (x instanceof MyPromise) {
        //规范2.3.2 如果x为MyPromise, 获取它的状态
        const state = x.state;
        if (state === PENDING) {
            //规范2.3.2.1 如果为pending,如果 x 处于等待态, promise 需保持为等待态直至 x 被执行或拒绝
            x.then(y => resolutionProcedure(promise, y), promise.reject);
        }else if (state === FULFILLED) {
            //规范2.3.2.1 如果为fulfilled, 用相同的value fulfill promise
            promise.resolve(x.value);
        }else {
            //规范2.3.2.2 如果为rejected, 用相同的reason reject promise
            promise.reject(x.reason);
        }
        return;
    }
    //规范2.3.3 如果x为对象或者函数
    if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
        try {
            // 规范2.3.3.1 获取x的then属性
            const then = x.then;
            if (typeof then === 'function') {
                //规范2.3.3.3 如果then 为函数,调用then,以x为this值,传入resolvePromise和rejectPromise两个回调函数
                then.call(x, resolvePromise, rejectPromise);
            }else {
                // x为普通对象和普通函数,直接resolve(x)
                promise.resolve(x);
            }
        }catch(e) {
            //规范2.3.3.3.4.1 调用then抛出异常时,如果resolvePromise或者rejectPromise已被调用,则忽略该异常
            if (!resolvePromise.called && !rejectPromise.called) {
                promise.reject(e);
            }
        }
    }else {
        // 规范2.3.4 x不为object或者function,直接resolve(x)
        promise.resolve(x);
    }
    
    function resolvePromise(y) {
        // 稍后实现
    }
    
    function rejectPromise(r) {
        // 稍后实现
    }
}

六、 resolvePromise、rejectPromise实现

function resolvePromise(y) {
    //规范2.3.3.3.3 如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了多次,
    //则优先采用首次调用并忽略剩下的调用
    if (!rejectPromise.called && !resolvePromise.called) {
        // 给函数增加called属性,并设置为true,表明已经被调用
        resolvePromise.called = true;
        //规范2.3.3.3.1 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
        resolutionProcedure(promise, y);
    }
}
function rejectPromise(r) {
    //规范2.3.3.3.3 如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了多次,
    //则优先采用首次调用并忽略剩下的调用
    if (!resolvePromise.called && !rejectPromise.called) {
        rejectPromise.called = true;
        //规范2.3.3.3.2 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
        promise.reject(r);
    }
}