手撕Promise,一文读懂Promise内部原理

68 阅读2分钟

废话不多说,直接上代码

// 先赞后看,已成习惯

// 定义状态码(这个不一定要和别人的一样,随便是啥都行)
const SUCCESS = "resolved";
const FAIL = "rejected";
const LOADING = "pending";

/**
 * 符合A+规范的,参考MDN
 */
const isPromiseLike = (promise) => {
    return (
        promise &&
        (typeof promise === "function" || typeof promise === "object") &&
        typeof promise.then === "function"
    );
};

class MyPromise {
    #promiseValue = undefined;
    #promiseStatus = LOADING;
    #taskList = [];

    constructor(task) {
        if (!task || typeof task !== "function") {
            return;
        }
        const resolve = (data) => {
            this.#changeStatus(SUCCESS, data);
        };
        const reject = (err) => {
            this.#changeStatus(FAIL, err);
        };
        try {
            task(resolve, reject);
        } catch (error) {
            reject(error);
        }
    }

    #changeStatus(status, value) {
        // 状态只允许改变一次,后续的改变不会处理
        if (#promiseStatus !== LOADING) {
            return;
        }
        this.#promiseStatus = status;
        this.#promiseValue = value;
        this.#run();
    }

    // 微队列
    #runMircoTask(callback) {
        // 如果支持微队列API,直接放到里面执行
        if (typeof queueMicrotask === 'function') {
            queueMicrotask(callback);
            return
        }
        // 如果支持MutationObserver
        if (MutationObserver) {
            const observer = new MutationObserver((mutations, observer) => {
                if (mutations[0]) {
                    callback?.();
                    observer.disconnect();
                }
            })
            const targetDom = document.createElement('div');
            observer.observe(targetDom, { attributes: true });
            targetDom.setAttribute('a', 'a');
            return
        }
        // 如果支持setTimeout
        if (typeof setTimeout === 'function') {
            setTimeout(callback, 0);
            return
        }
        // 都不支持,就直接调用吧
        callback?.();
    }

    #runTask(task, resolve, reject) {
        this.#runMircoTask(() => {
            if (typeof task === "function") {
                try {
                    const data = task(this.#promiseValue);
                    if (isPromiseLike(data)) {
                        data.then(resolve, reject);
                    } else {
                        resolve(data);
                    }
                } catch (error) {
                    reject(error);
                }
            } else {
                this.#promiseStatus === SUCCESS
                    ? resolve(this.#promiseValue)
                    : reject(this.#promiseValue);
            }
        });
    }

    #run() {
        // pending状态不执行回调函数,仅仅保存回调,将来当前Promise状态发生变化时会执行
        if (this.#promiseStatus === LOADING) {
            return;
        }
        while (this.#taskList.length > 0) {
            const [successCallback, errorCallback, resolve, reject] =
                this.#taskList.shift();
            if (this.#promiseStatus === SUCCESS) {
                this.#runTask(successCallback, resolve, reject);
            } else {
                this.#runTask(errorCallback, resolve, reject);
            }
        }
    }
    
    then(successCallback, errorCallback) {
        return new MyPromise((resolve, reject) => {
            this.#taskList.push([successCallback, errorCallback, resolve, reject]);
            this.#run();
        });
    }
    
    catch(errorCallback) {
        return new MyPromise((resolve, reject) => {
            // TODO
        });
    }
    
    static resolve(data) {
        return new MyPromise((_resolve) => {
            _resolve(data)
        })
    }
    
    static reject(data) {
        return new MyPromise((_resolve, _reject) => {
            _reject(data)
        })
    }
    
    /**
     * 确保所有Promise都成功完成才算成功,其中一个失败将会导致整个操作失败
     */
    static all(promises) {
        return new MyPromise((resolve, reject) => {
            if (!promises || !Array.isArray(promises) || !promises.length) {
                resolve([]);
                return
            }
            let countResolved = 0;
            const isAllResolved = () => promises.length === countResolved;
            const result = new Array(promises.length).fill(undefined);
            const check = () => {
                if (isAllResolved()) {
                    resolve(result);
                }
            }
            for(let i = 0; i < promises.length; i++) {
                const curPromise = promises[i];
                if (isPromiseLike(curPromise)) {
                    curPromise.then((data) => {
                        result[i] = data;
                        ++countResolved;
                        check();
                    }, (error) => {
                        reject(error);
                        // 不阻塞后续的promise执行,但是结果没意义
                        continue;
                    })
                } else {
                    result[i] = curPromise;
                    ++countResolved;
                    check();
                }
            }
        })
    }
    
    /**
     * 处理所有Promise的结果,无论它们是否成功,并将结果包裹成一个对应的数组,且永远不会失败
     */
    static allSettled(promises) {
        return new MyPromise((resolve) => {
            if (!promises || !Array.isArray(promises) || !promises.length) {
                resolve([]);
                return
            }
            let countResolved = 0;
            const isAllResolved = () => promises.length === countResolved;
            const result = new Array(promises.length).fill(undefined);
            const check = (status, data) => {
                ++countResolved;
                result[i] = { status, value: data };
                if (isAllResolved()) {
                    resolve(result);
                }
            }
            for(let i = 0; i < promises.length; i++) {
                const curPromise = promises[i];
                if (isPromiseLike(curPromise)) {
                    curPromise.then((data) => {
                        check(SUCCESS, data);
                    }, (error) => {
                        check(FAIL, error);
                    })
                } else {
                    check(SUCCESS, curPromise);
                }
            }
        })
    }
    
    /**
     * 出现第一个完成状态的promise就完成了
     */
    static race(promises) {
        return new MyPromise((resolve) => {
            if (!promises || !Array.isArray(promises) || !promises.length) {
                resolve([]);
                return
            }
            for(let i = 0; i < promises.length; i++) {
                const curPromise = promises[i];
                if (isPromiseLike(curPromise)) {
                    curPromise.then((data) => {
                        resolve(data);
                    }, (error) => {
                        resolve(error);
                    })
                } else {
                    resolve(curPromise);
                }
            }
        })
    }
}