手写promise之详详细细以及promise.all用变量计数的思考

529 阅读7分钟

直接上代码

Promise类和then方法

class DPromise {
    // 传入回调函数
    constructor(executor) {
        //保存状态
        this.DPromiseStatus = 'pending';
        //保存需要传递的值
        this.DPromiseResult = null;
        //保存函数,结构是数组套对象
        this.callbacks = [];
        const resolve = value => {
            //判断状态,保证promise状态改变后不能在更改状态
            if (this.DPromiseStatus === 'pending') {
                this.DPromiseStatus = 'fulfilled';
                this.DPromiseResult = value;
                queueMicrotask(() => {
                    this.callbacks.forEach(item => item.onfulfilled(this.DPromiseResult));
                });
            }
        };
        //判断状态,保证promise状态改变后不能在更改状态
        const reject = reason => {
            if (this.DPromiseStatus === 'pending') {
                this.DPromiseStatus = 'rejected';
                this.DPromiseResult = reason;
                queueMicrotask(() => {
                    this.callbacks.forEach(item => item.onrejected(this.DPromiseResult));
                });
            }
        };
        try {
            //执行回调函数,参数也是回调函数
            executor(resolve, reject);
        } catch (e) {
            //收集错误,错误信息通过reject传递
            reject(e);
        }
    }
    //then 方法
    then(onfulfilled, onrejected) {
        // then方法返回的也是promise对象
        //onfulfilled和onrejected都需要传入函数,如果不是函数,代码运行到函数调用会出错
        //如果传入非函数,则装换成函数,并将调用者的resolve参数传递下去
        if (typeof onfulfilled !== 'function') {
            onfulfilled = value => value;
        }
        //异常穿透,保证后面的catch或者then的onrejected可以捕获异常
        if (typeof onrejected !== 'function') {
            onrejected = reason => {
                throw reason;
            };
        }
        //获取promise对象,以便在函数内部使用
        const self = this;
        return new DPromise((resolve, reject) => {
            // 封装一个工具函数,确定then方法中onrejected和onfulfilled回调函数的返回值
            function rule(fnType) {
                try {
                    let result = fnType(self.DPromiseResult);
                    // 判断 then方法回调函数的返回值是否为promise对象
                    //如果是,则通过promise.then方法决定新promise的状态
                    if (result instanceof DPromise) {
                        result.then(
                            res => resolve(res),
                            err => reject(err)
                        );
                    } else {
                        // 如果不是promise对象,则通过resolve进行包装
                        resolve(result);
                    }
                } catch (e) {
                    //捕获异常
                    reject(e);
                }
            }
            //需要判断then方法调用者的状态,如果为fulfilled则执行,onfulfilled函数,
            if (this.DPromiseStatus === 'fulfilled') {
                //异步调用onfulfilled方法
                queueMicrotask(() => {
                    rule(onfulfilled);
                });
            }
            // 如果为rejected状态,则执行onrejected函数
            if (this.DPromiseStatus === 'rejected') {
                //异步调用onrejected
                queueMicrotask(() => {
                    rule(onrejected);
                });
            }
            //如果状态为pending则将两个回调函数保存在对象中,一般如果then调用者的resolve或reject异步执行后,相应调用保存的回调函数
            if (this.DPromiseStatus === 'pending') {
                this.callbacks.push({
                    onfulfilled() {
                        rule(onfulfilled);
                    },
                    onrejected() {
                        rule(onrejected);
                    },
                });
            }
        });
    }
  }

catch和finally

catch(onrejected) {
        //相当于then方法onrejected的语法糖
        return this.then(undefined, onrejected);
    }
    finally(onfinally) {
        // 调用then方法,在最后不管哪种状态,都执行回调函数
        this.then(
            () => {
                onfinally();
            },
            () => {
                onfinally();
            }
        );
    }

resolve和reject

static resolve(value) {
        return new DPromise((resolve, reject) => {
            //判断一下value值是否为promise对象,跟前面then方法中的工具函数一样
            if (value instanceof DPromise) {
                value.then(
                    res => resolve(res),
                    err => reject(err)
                );
            } else {
                resolve(value);
            }
        });
    }
    static reject(reason) {
        return new DPromise((resolve, reject) => {
            reject(reason);
        });
    }

all和allSettled

  static all(promises) {
        return new DPromise((resolve, reject) => {
            let count = 0;
            let result = [];
            for (let i = 0; i < promises.length; i++) {
                promises[i].then(
                    res => {
                        count++;
                        // 通过下标值可以确保返回结果的顺序不会被打乱
                        result[i] = res;
                        if (count === promises.length) {
                            resolve(result);
                        }
                    },
                    err => {
                        reject(err);
                    }
                );
            }
        });
    }
    static allSettled(promises) {
        //返回所有执行结果
        return new DPromise((resolve, reject) => {
            const result = [];
            let count = 0;
            promises.forEach((promise, index) => {
                promise.then(
                    res => {
                        count++;
                        result[index] = { status: 'fulfilled', value: res };
                        if (count === promises.length) {
                            resolve(result);
                        }
                    },
                    err => {
                        count++;
                        result[index] = { status: 'rejected', value: err };
                        if (count === promises.length) {
                            resolve(result);
                        }
                    }
                );
            });
        });
    }

为什么使用变量计数而不是数组下标值?

写到all方法突然产生的疑惑,经过思考->不能使用result.length来判断判断promises是否遍历完成。

  • 举个例子:如果promises中所有的promise对象,resolve外都包裹一层setTimeout定时器,而最后一个的定时器时间最短,那么遍历开始,进入.then方法,判断为pending状态,然后将then的onfulfilled回调函数保存在实例对象中,等到定时器结束,最后一个promise先执行resolve(),满足了result.length ===promises.length,返回的结果就是[empty,empty,empty,...,res],只有最后一个索引有值,其他全是空的,经过测试也证实我的想法,如果使用数组下标值来判断,确实会导致返回的结果是由数组最后一个值的执行时机决定的。 如下错误代码验证
static all(promises) {
        return new HYPromise((resolve, reject) => {
            const values = [];
            promises.forEach((promise, index) => {
                promise.then(
                    res => {
                        values[index] = res;
                        if (values.length === promises.length) {
                            resolve(values);
                        }
                    },
                    err => {
                        reject(err);
                    }
                );
            });
        });
    }
//测试代码
const p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(1111);
    }, 3000);
});
const p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(2222);
    }, 2000);
});
const p3 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(3333);
    }, 1000);
});

HYPromise.all([p1, p2, p3]).then(res => {
    console.log('res:', res);
});
Promise.all([p1, p2, p3]).then(res => {
    console.log('res:', res);
});

//打印结果
res: (3) [empty × 2, 3333]
res: (3) [1111, 2222, 3333]

race 和any

static race(promises) {
        return new DPromise((resolve, reject) => {
            promises.forEach(promise => {
                promise.then(
                    res => resolve(res),
                    err => reject(err)
                );
            });
        });
    }
    static any(promises) {
        //返回最先完成的resolve,或者返回全部的reject
        return new DPromise((resolve, reject) => {
            const result = [];
            let count = 0;
            promises.forEach((promise, index) => {
                promise.then(
                    res => {
                        resolve(res);
                    },
                    err => {
                        count++;
                        result[index] = err;
                        if (count === promises.length) {
                            reject(new AggregateError(result));
                        }
                    }
                );
            });
        });
    }

完整代码

class DPromise {
    // 传入回调函数
    constructor(executor) {
        //保存状态
        this.DPromiseStatus = 'pending';
        //保存需要传递的值
        this.DPromiseResult = null;
        //保存函数,结构是数组套对象
        this.callbacks = [];
        const resolve = value => {
            //判断状态,保证promise状态改变后不能在更改状态
            if (this.DPromiseStatus === 'pending') {
                this.DPromiseStatus = 'fulfilled';
                this.DPromiseResult = value;
                queueMicrotask(() => {
                    this.callbacks.forEach(item => item.onfulfilled(this.DPromiseResult));
                });
            }
        };
        //判断状态,保证promise状态改变后不能在更改状态
        const reject = reason => {
            if (this.DPromiseStatus === 'pending') {
                this.DPromiseStatus = 'rejected';
                this.DPromiseResult = reason;
                queueMicrotask(() => {
                    this.callbacks.forEach(item => item.onrejected(this.DPromiseResult));
                });
            }
        };
        try {
            //执行回调函数,参数也是回调函数
            executor(resolve, reject);
        } catch (e) {
            //收集错误,错误信息通过reject传递
            reject(e);
        }
    }
    //then 方法
    then(onfulfilled, onrejected) {
        // then方法返回的也是promise对象
        //onfulfilled和onrejected都需要传入函数,如果不是函数,代码运行到函数调用会出错
        //如果传入非函数,则装换成函数,并将调用者的resolve参数传递下去
        if (typeof onfulfilled !== 'function') {
            onfulfilled = value => value;
        }
        //异常穿透,保证后面的catch或者then的onrejected可以捕获异常
        if (typeof onrejected !== 'function') {
            onrejected = reason => {
                throw reason;
            };
        }
        //获取promise对象,以便在函数内部使用
        const self = this;
        return new DPromise((resolve, reject) => {
            // 封装一个工具函数,确定then方法中onrejected和onfulfilled回调函数的返回值
            function rule(fnType) {
                try {
                    let result = fnType(self.DPromiseResult);
                    // 判断 then方法回调函数的返回值是否为promise对象
                    //如果是,则通过promise.then方法决定新promise的状态
                    if (result instanceof DPromise) {
                        result.then(
                            res => resolve(res),
                            err => reject(err)
                        );
                    } else {
                        // 如果不是promise对象,则通过resolve进行包装
                        resolve(result);
                    }
                } catch (e) {
                    //捕获异常
                    reject(e);
                }
            }
            //需要判断then方法调用者的状态,如果为fulfilled则执行,onfulfilled函数,
            if (this.DPromiseStatus === 'fulfilled') {
                //异步调用onfulfilled方法
                queueMicrotask(() => {
                    rule(onfulfilled);
                });
            }
            // 如果为rejected状态,则执行onrejected函数
            if (this.DPromiseStatus === 'rejected') {
                //异步调用onrejected
                queueMicrotask(() => {
                    rule(onrejected);
                });
            }
            //如果状态为pending则将两个回调函数保存在对象中,一般如果then调用者的resolve或reject异步执行后,相应调用保存的回调函数
            if (this.DPromiseStatus === 'pending') {
                this.callbacks.push({
                    onfulfilled() {
                        rule(onfulfilled);
                    },
                    onrejected() {
                        rule(onrejected);
                    },
                });
            }
        });
    }
    catch(onrejected) {
        //相当于then方法onrejected的语法糖
        return this.then(undefined, onrejected);
    }
    finally(onfinally) {
        // 调用then方法,在最后不管哪种状态,都执行回调函数
        this.then(
            () => {
                onfinally();
            },
            () => {
                onfinally();
            }
        );
    }
    static resolve(value) {
        return new DPromise((resolve, reject) => {
            //判断一下value值是否为promise对象,跟前面then方法中的工具函数一样
            if (value instanceof DPromise) {
                value.then(
                    res => resolve(res),
                    err => reject(err)
                );
            } else {
                resolve(value);
            }
        });
    }
    static reject(reason) {
        return new DPromise((resolve, reject) => {
            reject(reason);
        });
    }
    static all(promises) {
        return new DPromise((resolve, reject) => {
            //为什么使用变量计数而不是result.length来判断判断promises是否遍历完成,举个例子:如果promises中所有的promise对象,resolve外都包裹一层setTimeout定时器,而最后一个的定时器时间最短,那么遍历开始,进入.then方法,判断为pending状态,然后将then的onfulfilled回调函数保存在实例对象中,等到定时器结束,最后一个promise先执行resolve(),满足了result.length ===promises.length,返回的结果就是[empty,empty,empty,...,res],只有最后一个索引有值,其他全是空的
            let count = 0;
            let result = [];
            for (let i = 0; i < promises.length; i++) {
                promises[i].then(
                    res => {
                        count++;
                        // 通过下标值可以确保返回结果的顺序不会被打乱
                        result[i] = res;
                        if (count === promises.length) {
                            resolve(result);
                        }
                    },
                    err => {
                        reject(err);
                    }
                );
            }
        });
    }
    static allSettled(promises) {
        //返回所有执行结果
        return new DPromise((resolve, reject) => {
            const result = [];
            let count = 0;
            promises.forEach((promise, index) => {
                promise.then(
                    res => {
                        count++;
                        result[index] = { status: 'fulfilled', value: res };
                        if (count === promises.length) {
                            resolve(result);
                        }
                    },
                    err => {
                        count++;
                        result[index] = { status: 'rejected', value: err };
                        if (count === promises.length) {
                            resolve(result);
                        }
                    }
                );
            });
        });
    }
    static race(promises) {
        return new DPromise((resolve, reject) => {
            promises.forEach(promise => {
                promise.then(
                    res => resolve(res),
                    err => reject(err)
                );
            });
        });
    }
    static any(promises) {
        //返回最先完成的resolve,或者返回全部的reject
        return new DPromise((resolve, reject) => {
            const result = [];
            let count = 0;
            promises.forEach((promise, index) => {
                promise.then(
                    res => {
                        resolve(res);
                    },
                    err => {
                        count++;
                        result[index] = err;
                        if (count === promises.length) {
                            reject(new AggregateError(result));
                        }
                    }
                );
            });
        });
    }
}
//测试代码
const p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(1111);
    }, 3000);
});
const p2 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(2222);
    }, 2000);
});
const p3 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(3333);
    }, 1000);
});

DPromise.all([p1, p2, p3]).then(
    res => {
        console.log('res:', res);
    },
    err => {
        console.log('err:', err);
    }
);
Promise.all([p1, p2, p3]).then(
    res => {
        console.log('res:', res);
    },
    err => {
        console.log('err:', err);
    }
);