手写Promise (二)

91 阅读7分钟

我们知道,Promise的then是可以有多个,多次调用的,比如:

let promise = new MyPromise((resolve, reject) => {
    resolve('成功...');
})

// 立即执行,resolve还未执行,所以status仍然是pendding
// 所以需要存储这些事态
promise.then((value) => {
    console.log(1, value);
}, (err) => {
    console.log(1, err);
});
// 由于支持链式调用,多个then回调,可能在状态还未变化之前,就已经执行了;
// 所以需要把回调数组缓存起来
promise.then((value) => {
    console.log(2, value);
}, (err) => {
    console.log(2, err);
});

promise.then((value) => {
    console.log(3, value);
}, (err) => {
    console.log(3, err);
});

此处运算的结果,

即:不仅可以多次调用then获取最终的运算结果,promise的状态也会保持一致;

5、那么这种情况,我们应该是在阶段一的基础,用一个数组来存储这些then的事件,在resolve、reject事件触发的时候,通过循环shift,把事件回调一个个执行完。

并且,Promise中的运算是支持异步的,那么如果:

let promise = new MyPromise((resolve, reject) => {
    // resolve('成功');
    setTimeout(() => {
        resolve('成功...');
    }, 2000);
    // reject('失败');
})

在阶段一的基础下,他在then调用的时候,是直接发起回调的。如果运算中添加了异步,此时Promise的状态仍然是pendding,那么就没办法触发回调。

6、在then增加最后一个状态判断,pendding的判断,将事件缓存起来

const PENDDING = 'pendding'; // 等待
const FULFILLED = 'fulfilled' // 成功
const REJECTED = 'rejected'; // 失败
class MyPromise {
    constructor (executor) {
        executor(this.resolve, this.reject);
    }
    // 默认等待
    status = PENDDING;
    // 成功之后的值
    value = undefined;
    // 失败原因
    reason = undefined;
    successCallBack = []
    failCallBack = [];
    // 定义箭头函数,由于使用是直接调用的,所以使用箭头函数,
    // 让this指向promise对象
    resolve = (value) => {
        // 不是等待,拒绝执行
        // 将状态更改为成功
        if (this.status !== PENDDING) {
            return;
        }
        this.status = FULFILLED;
        // 保存成功之后的值
        this.value = value;

        // 判断成功回调是否存在
        // if (this.successCallBack) {
        //     this.successCallBack(this.value);
        // }
        while (this.successCallBack.length) {
            this.successCallBack.shift()(this.value);
        }
    }
    reject = (reason) => {
        // 不是等待,拒绝执行
        if (this.status !== PENDDING) {
            return;
        }
        // 将状态更改为失败
        this.status = REJECTED;
        // 保存失败原因
        this.reason = reason;

        // 判断失败回调是否存在
        // if (this.failCallBack) {
        //     this.failCallBack(this.reason);
        // }
        
        while (this.failCallBack.length) {
            this.failCallBack.shift()(this.reason);
        }
    }
    then = (successCallBack, failCallBack) => {
        // 判断状态
        if (this.status === FULFILLED) {
            successCallBack(this.value);
        } else if (this.status === REJECTED) {
            failCallBack(this.reason);
        } else {
            // 等待
            // 由于异步,状态不一定在代码运行到then的时候,
            // 状态发生变化,所以此时,pendding,需要把回调存起来
            this.successCallBack.push(successCallBack);
            this.failCallBack.push(failCallBack);
        }
    }
}

通过在resolve、reject添加清空缓存事件的逻辑

while (this.successCallBack.length) {
    this.successCallBack.shift()(this.value);
}

在then添加存储回调事件的逻辑

this.successCallBack.push(successCallBack);
this.failCallBack.push(failCallBack);

来完成then的多次调用和异步调用,一举两得

那么,根据Promise的then的使用,发现他不仅可以多次调用,还支持链式调用: 链式调用不止是then的使用,而且是上一次then运算结果,传入下一次then;

promise.then((value) => {
    console.log(1, value);
    return 100;
}, (err) => {
    console.log(1, err);
}).then((value) => {
    console.log(1, value);
}, (err) => {
    console.log(1, err);
});

7、想要链式调用,就得return,那么then之后仍然是使用Promise的方法,那么then的return就应该return一个新的Promise对象,

那么then的代码最终实现如下:

then = (successCallBack, failCallBack) => {
    let promise2 = new MyPromise((resolve, reject) => {
        // 判断状态
        if (this.status === FULFILLED) {
            // 异步代码,为了获取promise2,用于判断then返回的res对象是否是自己本身,
            // 需要拦截
            setTimeout(() => {
                // setTimeout 是为了让promise实例化先赋值给promise2,
                // 这样子才可以被赋值的promise2,
                // promise2 初始值undefined
                let res = successCallBack(this.value);
                // 提供给下一个then调用
                // 判断res 是普通纸还是promise对象
                // 如果是普通值,是可以直接进行回传
                // 如果是promise,需要查看promise返回的结果
                // 根据结果成功,失败调用resolve或者是reject
                // resolve(res);
                this.resolvePromise(promise2, res, resolve, reject);
            }, 0)
        } else if (this.status === REJECTED) {
            setTimeout(() => {
                let res = failCallBack(this.reason);
                // reject(res);
                this.rejectPromise(promise2, res, resolve, reject);
            }, 0);
        } else {
            // 等待
            // 由于异步,状态不一定在代码运行到then的时候,
            // 状态发生变化,所以此时,pendding,需要把回调存起来
            this.successCallBack.push(() => {
                setTimeout(() => {
                    // setTimeout 是为了让promise实例化先赋值给promise2,
                    // 这样子才可以被赋值的promise2,
                    // promise2 初始值undefined
                    let res = successCallBack(this.value);
                    // 提供给下一个then调用
                    // 判断res 是普通纸还是promise对象
                    // 如果是普通值,是可以直接进行回传
                    // 如果是promise,需要查看promise返回的结果
                    // 根据结果成功,失败调用resolve或者是reject
                    // resolve(res);
                    this.resolvePromise(promise2, res, resolve, reject);
                }, 0)
            });
            this.failCallBack.push(() => {
                setTimeout(() => {
                    let res = failCallBack(this.reason);
                    // reject(res);
                    this.rejectPromise(promise2, res, resolve, reject);
                }, 0);
            });
        }
    });
    // 下一个then获取到的就是此时的promise2,而在promise2内部根据状态,
    // 回调resolve 或者reject来出发链式调用的then
    return promise2;
}
resolvePromise (promise2, res, resolve, reject) {
    if (res instanceof MyPromise) {
        res.then(resolve, reject);
    } else {
        resolve(res);
    }
}
rejectPromise (res, resolve, reject) {
    if (res instanceof MyPromise) {
        res.then(resolve, reject);
    } else {
        reject(res);
    }
}

这里增加了resolvePromise 、rejectPromise 方法,用来统一处理整个then重复的resolve、reject回调

8、由于返回的是一个新对象,如果没有添加setTimeout来异步处理,而是立即返回promise2,那么此时promise2是空的,因为还未赋值。只有在setTimeout回调之后,promise2才会被 new 赋值:

setTimeout(() => {
    let res = successCallBack(this.value);
    this.resolvePromise(promise2, res, resolve, reject);
}, 0)

9、then回调可以return 一个值,也支持return一个promise,所以需要判断是否 instanceof Promise,来手动执行触发then的resolve、reject状态,以便将上一个then的运算结果传给下一个then:

resolvePromise (promise2, res, resolve, reject) {
    if (res instanceof MyPromise) {
        // 是否promise对象,是则执行resolve,获取运算结果
        res.then(resolve, reject);
    } else {
        resolve(res);
    }
}

由于then可以回调,那么如果当then被误操作,回调了本身:

// 既然可以返回promise对象,那么如果返回自己本身呢?
var promise = new Promise((res, rej) => {res(100)});

var p1 = promise.then((value) => {console.log(value); return p1})
//  Chaining cycle detected for promise #<Promise>

此时,Pormise是会抛出错误的,所以:

10、我们也需要添加一个判断传入的promise是否是回调本身,并且进行拦截

resolvePromise、rejectPromise需要添加多一行判断:

resolvePromise (promise2, res, resolve, reject) {
    // res 是否属于promise
    if (promise2 === res) {
        // 自己返回自己, 直接报错
        return reject(new TypeError('chaining cycle detected for promise #<Promise>'));
    }
    // ...
}

11、为了防止整个执行过程出现错误,我们需要添加一个捕获,将错误捕获之后默认调用reject回调

最终

阶段二:实现了then的多次调用、链式调用。结果传递、错误拦截等

const PENDDING = 'pendding'; // 等待
const FULFILLED = 'fulfilled' // 成功
const REJECTED = 'rejected'; // 失败
class MyPromise {
    constructor (executor) {
        try {
            // 捕获错误
            executor(this.resolve, this.reject);
        }catch(err) {
            this.reject(err);
        }
    }
    // 默认等待
    status = PENDDING;
    // 成功之后的值
    value = undefined;
    // 失败原因
    reason = undefined;
    successCallBack = []
    failCallBack = [];
    // 定义箭头函数,由于使用是直接调用的,所以使用箭头函数,让this指向promise对象
    resolve = (value) => {
        // 不是等待,拒绝执行
        // 将状态更改为成功
        if (this.status !== PENDDING) {
            return;
        }
        this.status = FULFILLED;
        // 保存成功之后的值
        this.value = value;

        // 判断成功回调是否存在
        // if (this.successCallBack) {
        //     this.successCallBack(this.value);
        // }
        while (this.successCallBack.length) {
            this.successCallBack.shift()();
        }
    }
    reject = (reason) => {
        // 不是等待,拒绝执行
        if (this.status !== PENDDING) {
            return;
        }
        // 将状态更改为失败
        this.status = REJECTED;
        // 保存失败原因
        this.reason = reason;

        // 判断失败回调是否存在
        // if (this.failCallBack) {
        //     this.failCallBack(this.reason);
        // }
        
        while (this.failCallBack.length) {
            this.failCallBack.shift()();
        }
    }
    then = (successCallBack, failCallBack) => {
        successCallBack = successCallBack ? successCallBack : value => value;
        failCallBack = failCallBack ? failCallBack : value => value;
        let promise2 = new MyPromise((resolve, reject) => {
            // 判断状态
            if (this.status === FULFILLED) {
                // 异步代码,为了获取promise2,用于判断then返回的res对象是否是自己本身,
                // 需要拦截
                setTimeout(() => {
                    // setTimeout 是为了让promise实例化先赋值给promise2,
                    // 这样子才可以被赋值的promise2,
                    // promise2 初始值undefined
                    try {
                        let res = successCallBack(this.value);
                        // 提供给下一个then调用
                        // 判断res 是普通纸还是promise对象
                        // 如果是普通值,是可以直接进行回传
                        // 如果是promise,需要查看promise返回的结果
                        // 根据结果成功,失败调用resolve或者是reject
                        // resolve(res);
                        this.resolvePromise(promise2, res, resolve, reject);
                    } catch (err) {
                        reject(err);
                    }
                }, 0)
            } else if (this.status === REJECTED) {
                setTimeout(() => {
                    try {
                        let res = failCallBack(this.reason);
                        // reject(res);
                        this.resolvePromise(promise2, res, resolve, reject);
                    } catch (err) {
                        reject(err);
                    }
                }, 0);
            } else {
                // 等待
                // 由于异步,状态不一定在代码运行到then的时候,
                // 状态发生变化,所以此时,pendding,需要把回调存起来
                this.successCallBack.push(() => {
                    setTimeout(() => {
                        // setTimeout 是为了让promise实例化先赋值给promise2,
                        // 这样子才可以被赋值的promise2,
                        // promise2 初始值undefined
                        try {
                            let res = successCallBack(this.value);
                            // 提供给下一个then调用
                            // 判断res 是普通纸还是promise对象
                            // 如果是普通值,是可以直接进行回传
                            // 如果是promise,需要查看promise返回的结果
                            // 根据结果成功,失败调用resolve或者是reject
                            // resolve(res);
                            this.resolvePromise(promise2, res, resolve, reject);
                        } catch (err) {
                            reject(err);
                        }
                    }, 0)
                });
                this.failCallBack.push(() => {
                    setTimeout(() => {
                        try {
                            let res = failCallBack(this.reason);
                            // reject(res);
                            this.resolvePromise(promise2, res, resolve, reject);
                        } catch (err) {
                            reject(err);
                        }
                    }, 0);
                });
            }
        });
        // 下一个then获取到的就是此时的promise2,而在promise2内部根据状态,
        // 回调resolve 或者reject来出发链式调用的then
        return promise2;
    }
    resolvePromise (promise2, res, resolve, reject) {
        // res 是否属于promise
        if (promise2 === res) {
            // 自己返回自己, 直接报错
            return reject(new TypeError('chaining cycle detected for promise #<Promise>'));
        }
        if (res instanceof MyPromise) {
            res.then(resolve, reject);
        } else {
            resolve(res);
        }
    }
    rejectPromise (res, resolve, reject) {
        // res 是否属于promise
        if (promise2 === res) {
            // 自己返回自己, 直接报错
            return reject(new TypeError('chaining cycle detected for promise #<Promise>'));
        }
        if (res instanceof MyPromise) {
            res.then(resolve, reject);
        } else {
            reject(res);
        }
    }
}

文章内容输出来源:拉勾教育Java高薪训练营;