Promise的实现思路

144 阅读2分钟

function Promise(executor) {
    this.status = "pending"; // 状态值,实例状态(pending,fulfilled,rejected)
    this.value = null; //保存resolve的处理值
    this.reason = null; //保存reject的处理值
    this.onfulfilledArray = []; // resolve 的回调函数
    this.onrejectedArray = []; // reject 的回调函数

    let resolve = (value) => {
        if (value instanceof Promise) {
            return value.then(resolve, reject);
        }
        setTimeout(() => {
            if (this.status === "pending") {
                this.value = value;
                this.status = "fulfilled";
                this.onfulfilledArray.forEach((func) => {
                    func(value);
                });
            }
        });
    };
    let reject = (reason) => {
        setTimeout(() => {
            if (this.status === "pending") {
                this.reason = reason;
                this.status = "rejected";
                this.onrejectedArray.forEach((func) => {
                    func(reason);
                });
            }
        });
    };

    //  在构造函数中如果出错,将自动触发 Promise 实例状态变为 rejected
    try {
        executor(resolve, reject);
    } catch (error) {
        reject(e);
    }
}
/**
 *
 * @param promise2 返回的Promise 实例
 * @param result onfulfilled 或onrejected 函数的返回值
 * @param resolve promise2 的resolve 方法
 * @param reject promise2 的 reject 方法
 */
const resolvePromise = (promise2, result, resolve, reject) => {
    // 当 result和 Promise2想等时,也就是 onfulfilled 返回 Promise2时 执行reject
    if (result === promise2) {
        reject(new TypeError("error due to circular reference"));
    }
    // 是否已经执行过 onfulfilled 或onrejected
    let consumed = false;
    let thenable;
    if (result instanceof Promise) {
        if (result.status === "pending") {
            result.then(function (data) {
                resolvePromise(promise2, data, resolve, reject);
            }, reject);
        } else {
            result.then(resolve, reject);
        }
        return;
    }

    let isComplexResult = (target) =>
        (typeof target === "function" || typeof target === "object") &&
        target !== null;

    // 如果返回的是疑似 Promise 类型
    if (isComplexResult(result)) {
        try {
            thenable = result.then;
            // 判断返回值是否是 Promise 类型
            if (typeof thenable === "function") {
                thenable.call(
                    result,
                    function (data) {
                        if (consumed) {
                            return;
                        }
                        consumed = true;
                        return resolvePromise(promise2, data, resolve, reject);
                    },
                    function (error) {
                        if (consumed) {
                            return;
                        }
                        consumed = true;
                        return reject(error);
                    }
                );
            } else {
                resolve(result);
            }
        } catch (error) {
            if (consumed) {
                return;
            }
            consumed = true;
            return reject(e);
        }
    } else {
        resolve(result);
    }
};

Promise.prototype.then = function (onfulfilled, onrejected) {
    let promise2;
    if (this.status === "fulfilled") {
        return (promise2 = new Promise((resolve, reject) => {
            setTimeout(() => {
                try {
                    let result = onfulfilled(this.value);
                    resolvePromise(promise2, result, resolve, reject);
                } catch (error) {
                    reject(error);
                }
            });
        }));
    }

    if (this.status === "rejected") {
        return (promise2 = new Promise((resolve, reject) => {
            setTimeout(() => {
                try {
                    let result = onrejected(this.value);
                    resolvePromise(promise2, result, resolve, reject);
                } catch (error) {
                    reject(error);
                }
            });
        }));
    }

    if (this.status === "pending") {
        return (promise2 = new Promise((resolve, reject) => {
            this.onfulfilledArray.push(() => {
                try {
                    let result = onfulfilled(this.value);
                    resolvePromise(promise2, result, resolve, reject);
                } catch (error) {
                    reject(error);
                }
            });

            this.onrejectedArray.push(() => {
                try {
                    let result = onrejected(this.reason);
                    resolvePromise(promise2, result, resolve, reject);
                } catch (error) {
                    reject(error);
                }
            });
        }));
    }
};

Promise.prototype.catch = function (catchFunc) {
    return this.then(null, catchFunc);
};

Promise.resolve = function (value) {
    return new Promise((resolve, reject) => {
        resolve(value);
    });
};

Promise.reject = function (value) {
    return new Promise((resolve, reject) => {
        reject(value);
    });
};

Promise.all = function (promiseArray) {
    if (!Array.isArray(promiseArray)) {
        throw new TypeError("The arguments should be an array!");
    }
    return new Promise((resolve, reject) => {
        try {
            let resultArray = [];
            const length = promiseArray.length;
            for (let i = 0, item; (item = promiseArray[i++]);) {
                item.then((data) => {
                    resultArray.push(data);
                    if (resultArray.length === length) {
                        resolve(resultArray);
                    }
                }, reject);
            }
        } catch (error) {
            reject(error);
        }
    });
};

Promise.race = function (promiseArray) {
    if (!Array.isArray(promiseArray)) {
        throw new TypeError("The arguments should be an array!");
    }
    return new Promise((resolve, reject) => {
        try {
            for (let i = 0, item; (item = promiseArray[i++]);) {
                item.then(resolve, reject);
            }
        } catch (error) {
            reject(error);
        }
    });
};

let cs = new Promise((res, err) => {
    setTimeout(() => {
        res("da");
    }, 2000);
});
let cs2 = new Promise((res, err) => {
    setTimeout(() => {
        res("da2");
    }, 5000);
});

Promise.all([cs, cs2]).then((data) => {
    console.log(data);
});