一文解决Promise

48 阅读2分钟

什么是Promise

在现代开发过程中,异步请求已经成为我们无法避免的问题。之前解决异步请求都是通过回调函数的方式,但是这会造成回调地狱的问题。Promise出现之后,我们可以通过链式调用能够很轻松去避免回调过深的问题,同时Promise也给我们提供了非常良好的一些方法,可以供我们去使用。例如 Promise.all(),Promise.allSettled(),Promise.any(),Promise.race()这四个单例方法,那么今天就给大家手写一下Promise以及他提供的这些方法

Promise

class MyPromise {
    constructor(callback) {
        this.state = "pending";
        this.value = undefined;
        this.reason = undefined;

        this.onResolveCallbacks = [];
        this.onRejectCallbacks = [];

        this.resolve = (value) => {
            if (this.state === "pending") {
                this.state = "fullfilled";
                this.value = value;
                this.onResolveCallbacks.forEach(fn => fn());
            }
        }

        this.reject = (reason) => {
            if (this.state === "pending") {
                this.state = "reject";
                this.reason = reason;
                this.onRejectCallbacks.forEach(fn => fn());
            }
        }
        callback(this.resolve, this.reject);
    }

    then(onResolved, onRejected) {
        onResolved = typeof onResolved === 'function' ? onResolved : (value) => value;
        onRejected = typeof onRejected === 'function' ? onRejected : (reason) => { throw reason };

        const newPromise = new MyPromise((resolve, reject) => {
            if (this.state === "fullfilled") {
                try {
                    const x = onResolved(this.value);
                    resolve(x);
                } catch (err) {
                    reject(err);
                }
            }

            if (this.state === "reject") {
                try {
                    // 执行 onRejected 回调函数
                    const x = onRejected(this.reason);
                    // 处理返回值
                    resolve(x);
                } catch (err) {
                    reject(err);
                }
            }

            if (this.state === "pending") {
                this.onResolveCallbacks.push(() => {
                    try {
                        // 
                        const x = onResolved(this.value);
                        resolve(x);
                    } catch (error) {
                        reject(error)
                    }
                })

                this.onRejectCallbacks.push(() => {
                    try {
                        const x = onRejected(this.reason);
                        resolve(x);
                    } catch (error) {
                        reject(error)
                    }
                })
            } else {
                this.onRejectCallbacks = [];
                this.onResolveCallbacks = [];
            }
        })
        return newPromise;
    }

    catch(onRejected) {
        return this.then(null, onRejected);
    }

    finally(callback) {
        return this.then(
            value => resolve(callback()).then(() => value),
            reason => resolve(callback()).then(() => { throw reason })
        );
    }

    static all(arr) {
        if (!Array.isArray(arr)) {
            throw new Error('param is not array')
        }
        return new MyPromise((resolve, reject) => {
            let result = [];
            let count = 0;
            arr.forEach((item, index) => {
                if (item instanceof MyPromise) {
                    item.then(res => {
                        result[index] = res;
                        count++;
                        if (count === arr.length) {
                            return resolve(result);
                        }
                    }, err => {
                        return reject(err);
                    })
                } else {
                    result[index] = item;
                    count++;
                    if (count === arr.length) {
                        return resolve(result);
                    }
                }
            })
        })
    }

    static allSettled(arr) {
        if (!Array.isArray(arr)) {
            throw new Error('param is not array');
        }

        return new MyPromise((resolve, reject) => {
            let result = [];
            let count = 0;
            arr.forEach((item, index) => {
                if (item instanceof MyPromise) {
                    item.then(res => {
                        result[index] = res;
                        count++;
                        if (count === arr.length) {
                            return resolve(result);
                        }
                    }, err => {
                        result[index] = err;
                        count++;
                        if (count === arr.length) {
                            return resolve(result);
                        }
                    })
                } else {
                    result[index] = item;
                    count++;
                    if (count === arr.length) {
                        return resolve(result);
                    }
                }
            })
        })
    }

    static race(arr) {
        if (!Array.isArray(arr)) {
            throw new Error('param is not array');
        }
        return new MyPromise((resovle, reject) => {
            arr.forEach(item => {
                if (item instanceof MyPromise) {
                    item.then(res => {
                        return resovle(res);
                    }, err => {
                        return reject(err);
                    })
                } else {
                    return resovle(item)
                }
            })
        })
    }

    static any(arr) {
        if (!Array.isArray(arr)) {
            throw new Error('param is not array')
        }
        return new MyPromise((resolve, reject) => {
            let count = 0;
            let rejects = [];
            arr.forEach((item, index) => {
                if (item && typeof item.then === 'function') {
                    item.then(data => {
                        return resolve(data) // 使用最先成功的结果
                    }, err => {
                        rejects[index] = err;
                        count++;
                        if (count === arr.length) {
                            return reject(rejects);
                        }
                    })
                } else {
                    // 处理普通值,直接成功
                    return resolve(item);
                }
            })
        })
    }
}

上述是一个完整的Promise 代码,那么下面我解析一下这段代码。

constructor

    constructor(callback) {
        this.state = "pending";
        this.value = undefined;
        this.reason = undefined;

        this.onResolveCallbacks = [];
        this.onRejectCallbacks = [];

        this.resolve = (value) => {
            if (this.state === "pending") {
                this.state = "fullfilled";
                this.value = value;
                this.onResolveCallbacks.forEach(fn => fn());
            }
        }

        this.reject = (reason) => {
            if (this.state === "pending") {
                this.state = "reject";
                this.reason = reason;
                this.onRejectCallbacks.forEach(fn => fn());
            }
        }
        callback(this.resolve, this.reject);
    }
   

在构造函数中我们接受一个callback 作为参数,同时分别定义了 state,value,reason,onResolveCallbacks,onRejectCallbacks,resolve,reject这些元素,他们分别对应Promise的状态、成功值、失败原因、成功后的回调列表、失败后的回调列表等等。