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

2019-04-11  本文已影响0人  天上月丶
// 表示状态,便于后期维护
const PENDING = 'pending';
const RESOLVED = 'resolved';
const REJECTED = 'rejected';

function MyPromise(fn) {
    // 因代码可能会异步执行,用于获取正确的this对象
    const that = this;
    // 初始状态为pending
    that.state = PENDING;
    // 用于保存resolve 或者 reject 中传入的值
    that.value = null;
    // 用于保存then中的回调,因为当执行完Promise时状态可能还是等待中,
    // 这时候应该把then中的回调保存起来用户状态改变时使用
    that.resolvedCallbacks = [];
    that.rejectedCallbacks = [];

    // 首先两个函数都得判断当前状态是否是等待中,因为只有等待状态可以改变状态
    // 将当前状态更改为对应状态,并且将传入的值赋值给value
    // 遍历回调数组并执行
    function resolve(value) {
        // 首先需要判断传入的值是否为Promise类型
        if(value instanceof MyPromise) {
            return value.then(resolve, reject)
        }
        // 为了保证函数执行顺序,需要将函数整体代码用setTimeout包裹起来
        setTimeout(() => {
            if(that.state === PENDING) {
                that.state = RESOLVED;
                that.value = value;
                that.resolvedCallbacks.map(cb => cb(that.value));
            }
        }, 0);
    }

    function reject(value) {
        setTimeout(() => {
            if(that.state === PENDING) {
                that.state = REJECTED;
                that.value = value;
                that.rejectedCallbacks.map(cb => cb(that.value));
            }
        }, 0);
    }

    // 执行传入的参数并且将之前的两个函数当做参数传进去
    // 注意: 可能执行函数过程中会遇到错误,需要捕获错误并执行reject函数
    try{
        fn(resolve, reject)
    } catch (e) {
        reject(e);
    }
}


MyPromise.prototype.then = function(onFulfilled, onRejected) {
    const that = this;
    // 首先判断两个参数是否为函数类型,因为这两个参数时可选参数
    // 当参数不是函数类型时,需要创建一个函数赋值给对应的参数,同时也实现了透传
    // eg: Promise.resolve(4).then().then(value => console.log(value))
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v;
    onRejected = typeof onRejected === 'function' ? onRejected : r => { throw r; };

    let promise2;

    // 判断状态
    // 当状态不是等待态时,就去执行相应的函数。
    // 如果状态是等待态的话,就往回调函数中push函数
    if(that.state === PENDING) {
        // 返回一个新的Promise对象,并在Promise中传入了一个函数
        // 函数的基本逻辑和之前一样,往回调数组中push函数
        // 同样,在执行函数的过程中可能会遇到错误,所以使用了try ... catch 包裹
        // 规范规定,执行onFulfilled 或者 onRejected 函数时会返回一个X,
        // 并且执行Promise解决过程,这是为了不同的Promise都可以兼容使用
        return (promise2 = new MyPromise((resolve, reject) => {
            that.resolvedCallbacks.push(() => {
                try {
                    const x = onFulfilled(that.value);
                    resolutionProcedure(promise2, x, resolve, reject);
                } catch (e) {
                    reject(e);
                }
            });

            that.rejectedCallbacks.push(() => {
                try {
                    const x = onRejected(that.value);
                    resolutionProcedure(promise2, x, resolve, reject);
                } catch (e) {
                    reject(e);
                }
            })
        }));
    }

    if(that.state === RESOLVED) {
        // 规范规定,传入的函数体需要异步执行
        return (promise2 = new MyPromise((resolve, reject) => {
            setTimeout(() => {
                try {
                    const x = onFulfilled(that.value);
                    resolutionProcedure(promise2, x, resolve, reject);
                } catch (e) {
                    reject(e);
                }
            })
        }));
        // onFulfilled(that.value);
    }

    if(that.state === REJECTED) {
        return (promise2 = new MyPromise((resolve, reject) => {
            setTimeout(() => {
                try {
                    const x = onRejected(that.value);
                    resolutionProcedure(promise2, x, resolve, reject);
                } catch (e) {
                    reject(e);
                }
            })
        }))
        // onRejected(that.value)
    }

    function resolutionProcedure(promise2, x, resolve, reject) {
        // 规范规定 x 不能与 promise2 相等,这样会发生循环引用的问题
        if(promise2 === x) {
            return reject(new TypeError('Error'));
        }

        // 如果x 为Promise 的话,需要判断以下几个情况
        // 如果x处于等待态,Promise 需保持为等待态至 x 被执行或者拒绝
        // 如果 x 处于其他状态,则用相同的值处理Promise
        if(x instanceof MyPromise) {
            x.then(function(value) {
                resolutionProcedure(promise2, value, resolve, reject);
            }, reject)
        }

        // 判断是否已经调用过函数
        let called = false;
        // 判断 x 是否为对象或者函数,如果都不是的话,将 x 传入 resolve 中
        if(x !== null && (typeof x === 'object' || typeof x === 'function')) {
            try {
                // 如果 x 是对象或者函数的话,先把  x.then  赋值给 then, 然后再判断  then 的类型
                let then = x.then;
                if(typeof then === 'function') {
                    // 如果then 是函数类型的话,就将  x 作为函数的作用域  this 调用,并且传递两个回调函数作为参数,
                    // 第一个参数叫做 resolvePromise, 第二个参数为 rejectPromise, 两个回调函数都需要判断是否已经执行过函数,
                    // 然后进行相应的逻辑
                    then.call(x,
                        y => {
                            if(called) return;
                            called = true;
                            resolutionProcedure(promise2, y, resolve, reject);
                        },
                        e => {
                            if(called) return;
                            called = true;
                            reject(e);
                        }
                    )
                    // 如果不是函数类型的话,就将x传入 resolve 中
                } else {
                    resolve(x);
                }
            } catch (e) {
                // 以上代码执行的过程中如果抛错了,将错误传入 reject 函数中
                if(called) return;
                called = true;
                reject(e);
            }
        } else {
            resolve(x);
        }
    }
};

eg :

new MyPromise((resolve, reject) => {
    setTimeout(() => {
        resolve(1);
    }, 0)
 }).then(value => {
    console.log(value)
 });
上一篇下一篇

猜你喜欢

热点阅读