手写 Promise

一、前言

Promise 的手写也算是经典的前端题目了,这得要求你对 Promise 非常了解才可以。
Promise 详情的 API 请点这里,这里我简单讲下两个静态方法:

  • Promise.reject():返回一个以给定值解析后的Promise 对象
  • Promise.resolve():返回一个带有拒绝原因的Promise对象

还有一件事就是,Promise 是自己带有状态的,共计三种:pendingfulfilledrejected
Promise 刚开始创建时,状态为pending。一旦你的 Promise 执行了 resolve 或者 reject 回调,那么 Promise 后续状态只能为fulfilledrejected的其中一种。
注意 Promise 的原型方法:

  • Promise.prototype.then():有两个参数,第一个处理fulfilled状态,第二个处理rejected状态
  • Promise.prototype.catch():有一个参数,只处理rejected状态
  • Promise.prototype.finally():有一个参数,无论fulfilled还是rejected,都会执行;finally() 经过测试,可以放在 then() 或者 catch() 之前,后面的 then() 或者 catch() 还是会执行。也就是说,finally() 并不会终止后续的处理,并且可以多次设置。

在 Promise 的处理链中(即 then()、catch() 、finally() 组成的链式调用),这有个小例子:

let example=new Promise();
example.then().catch().finally().then().finally(); 
// 这个就是 Promise 的处理链,pending 状态结束后,处理链会依照顺序依次调用 then() 或者 catch() 或者 finally()
// 举个例子,如果 Promise 状态为 fulfilled,处理到 catch() 时会跳过,进入下一个处理,此时的 catch() 便会失效 
// catch() 能传入两个参数,能处理 fulfilled 状态或者 rejected 状态
// finally() 无论 Promise 是那种状态,当处理到这个位置上时都会执行,但是回调函数没有参数

二、代码实现

const PENDING = "pending";          // Promise 执行状态
const FULFUILLED = "fulfuilled";    // Promise 完成状态
const REJECTED = "rejected";        // Promise 失败状态
class MyPromise {
    __state = PENDING;  // 状态
    __callbacks = [];   // 回调数组,完成或者失败将要调用的
    __value = null;     // 正确的值
    __reason = null;    // 拒绝的值

    static resolve(arg) {
        return new MyPromise(resolve => {
            resolve(arg);
        })
    }

    static reject(arg) {
        return new MyPromise((_, reject) => {
            reject(arg);
        });
    }

    constructor(callBack) {
        try {
            callBack((arg) => {
                this.__value = arg;
                this.__state = FULFUILLED;
                this.__main();
            }, (arg) => {
                this.__reason = arg;
                this.__state = REJECTED;
                this.__main();
            });
        } catch (e) {
            this.__reason = e;
            this.__state = REJECTED;
            this.__main();
        }
    }

    __mainIsRunning = false;
    __main() {
        // 确保 MyPromise 的主体方法只能被执行一次
        if(this.__mainIsRunning){
            return;
        }
        this.__mainIsRunning=true;
        // 异步处理,同时也是为了 then、catch、finally 能够在此处 __main() 的主体代码执行前,先一步将传入的回调函数组册进入 __callbacks
        setTimeout(() => {
            for (let callBack of this.__callbacks) {
                const func = callBack[this.__state];
                func && func();
            }
            if (this.__state === REJECTED) {
                console.error("未捕捉的错误(in MyPromise)", this.__reason);
            }
        })
    }

    /**
     * 回调函数包装工厂
     * 根据 FULFUILLED 或者 REJECTED 状态,使用传入的回调函数进行处理
     * @param {Function} func 注册的回调函数
     * @param {"fulfuilled"|"rejected"} state 状态
     * @returns 
     */
    __callbackFactory(func, state) {
        return () => {
            if (typeof func === "function") {
                try {
                    const ans = func(state === FULFUILLED ? this.__value : this.__reason);
                    // 函数返回结果为 MyPromise 类型, 那么将其状态转移到正在运行的 MyPromise 主体
                    if (ans instanceof MyPromise) {
                        this.__state = ans.__state;
                        if (this.__state === FULFUILLED) {
                            this.__value = ans.__value;
                        } else {
                            // 如果返回的 MyPromise 的状态为 REJECTED,必须重置其为 FULFUILLED,不然会自动在控制台报错
                            // 原因是 MyPromise.prototype.__main()
                            ans.__state = FULFUILLED;
                            this.__reason = ans.__reason;
                        }
                    } else {
                        // 如果函数返回结果不是 MyPromise,那么状态必定为 FULFUILLED
                        this.__value = ans;
                        this.__state = FULFUILLED;
                    }
                } catch (e) {
                    // 捕捉回调函数运行中的错误,此时状态为REJECTED
                    this.__reason = e;
                    this.__state = REJECTED;
                }
            }
        }
    }

    then(fullfilledCallback, rejectedCallback) {
        const callback = {
            [FULFUILLED]: this.__callbackFactory(fullfilledCallback, FULFUILLED),
            [REJECTED]: this.__callbackFactory(rejectedCallback, REJECTED),
        }
        this.__callbacks.push(callback);
        return this;
    }

    catch (rejectedCallback) {
        const callback = {
            [FULFUILLED]: null,
            [REJECTED]: this.__callbackFactory(rejectedCallback, REJECTED),
        }
        this.__callbacks.push(callback);
        return this;
    };

    finally(finnallycallBack) {
        finnallycallBack = typeof finnallycallBack === "function" ? finnallycallBack : null;
        const callback = {
            [FULFUILLED]: finnallycallBack,
            [REJECTED]: finnallycallBack,
        }
        this.__callbacks.push(callback);
        return this;
    }
}


三、效果

    // 原生测试
    new Promise((resolve, reject) => {
        setTimeout(() => reject(1), 1000);
    }).then((res) => {
        console.log("原生测试 then(1):", res);
        return Promise.reject(res + 1);
    }, res => {
        console.log("原生测试 cath(1):", res);
        return Promise.reject(res + 1);
    }).catch(res => {
        console.log("原生测试 catch(2):", res);
        return Promise.resolve(res + 1);
    }).finally((res) => {
        console.log("原生测试 finally(1):", res)
    }).finally((res) => {
        console.log("原生测试 finally(2):", res)
    }).then(res => {
        console.log("原生测试 then(3):", res);
        return Promise.reject()
    }).finally(() => {
        console.log("原生测试 finally(4):原生Promise测试结束!");
    })

    // 自定义测试
    new MyPromise((resolve, reject) => {
        setTimeout(() => reject(1), 1000);
    }).then((res) => {
        console.log("自定义测试 then(1):", res);
        return MyPromise.reject(res + 1);
    }, res => {
        console.log("自定义测试 cath(1):", res);
        return MyPromise.reject(res + 1);
    }).catch(res => {
        console.log("自定义测试 catch(2):", res);
        return MyPromise.resolve(res + 1);
    }).finally((res) => {
        console.log("自定义测试 finally(1):", res)
    }).finally((res) => {
        console.log("自定义测试 finally(2):", res)
    }).then(res => {
        console.log("自定义测试 then(3):", res);
        return MyPromise.reject()
    }).finally(() => {
        console.log("自定义测试 finally(4):自定义Promise测试结束!");
    })

效果图:

四、总结

手写的 Promise 用的定时器开始运行,当然优先级不如原生 Promise,毕竟 Promise 完成后是 micro task。
Promise 手写不难,还有就是 Promise 其他的静态方法现实其实是最简单的。
注意,Promise.reject() 是异步抛出错误,用 try/catch 是捕捉不到的,因为它只能捕捉同步抛出的错误!
关键点:

  • 创建 Promise 的过程中,传入的回调函数是马上执行的,不会在事件循环中执行,且resolve、reject不会终止该回调的执行
  • Promise 自身拥有状态;
  • Promise 自身拥有对 rejected 状态或者 fulfilled 状态的状态数据保存,我的代码中 rejected 状态是用变量 __reason 保存,fulfilled 状态是用变量 __value 保存的;
  • Promise pending 状态结束后,就没有 pending 状态了,只有 fulfilled 或者 rejected 状态 。Promise.reject()、Promise.resolve() 是已经指定了fulfilled 或者 rejected 状态。
posted @ 2021-09-17 16:15  Sebastian·S·Pan  阅读(339)  评论(0编辑  收藏  举报