菜单 学习猿地 - LMONKEY

VIP

开通学习猿地VIP

尊享10项VIP特权 持续新增

知识通关挑战

打卡带练!告别无效练习

接私单赚外块

VIP优先接,累计金额超百万

学习猿地私房课免费学

大厂实战课仅对VIP开放

你的一对一导师

每月可免费咨询大牛30次

领取更多软件工程师实用特权

入驻
227
0

手写Promise2

原创
05/13 14:22
阅读数 34780

Promise的then方法是可以被链式调用的, 后面then方法的回调函数拿到值的是上一个then方法的回调函数的返回值。

要实现then方法的链式调用,首先需要保存每一个then方法都返回一个Promise对象:

 然后将拿到的上一个then方法的返回值,传递给下一个then方法的回调函数:

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

class MyPromise {
    constructor(executor) {
        executor(this.resolve, this.reject)
    }
    status = PENDING;
    value = undefined;
    reason = undefined;
    successCallback = []; 
    failCallback = []; 
    resolve = value => {
        if (this.status !== PENDING) return;
        this.status = FULFILLED;
        this.value = value;
        while(this.successCallback.length) this.successCallback.shift()(this.value)
    }
    reject = reason => {
        if (this.status !== PENDING) return;
        this.status = REJECTED;
        this.reason = reason;
        while(this.failCallback.length) this.failCallback.shift()(this.reason)
    }
    then(successCallback, failCallback) {
        let promise2 = new MyPromise((resolve,reject) => {
            if (this.status === FULFILLED) {
                let x = successCallback(this.value);
                resolve(x)
            } else if (this.status === REJECTED) {
                failCallback(this.reason);
            } else {
                this.successCallback.push(successCallback);
                this.failCallback.push(failCallback);
            }
        });
        return promise2
    }
}
module.exports = MyPromise;

  

 再上面的链式调用中,我们的then方法返回的是一个普通值。如果返回的是一个Promise对象,则需要先判断该对象的状态,然后根据状态来决定调用resolve 还是调用reject。这里将这个过程提取为一个公共方法resolvePromise():

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

class MyPromise {
    constructor(executor) {
        executor(this.resolve, this.reject)
    }
    status = PENDING;
    value = undefined;
    reason = undefined;
    successCallback = []; 
    failCallback = []; 
    resolve = value => {
        if (this.status !== PENDING) return;
        this.status = FULFILLED;
        this.value = value;
        while(this.successCallback.length) this.successCallback.shift()(this.value)
    }
    reject = reason => {
        if (this.status !== PENDING) return;
        this.status = REJECTED;
        this.reason = reason;
        while(this.failCallback.length) this.failCallback.shift()(this.reason)
    }
    then(successCallback, failCallback) {
        let promise2 = new MyPromise((resolve,reject) => {
            if (this.status === FULFILLED) {
                let x = successCallback(this.value);
                resolvePromise(x, resolve, reject)
            } else if (this.status === REJECTED) {
                let x = failCallback(this.reason);
                resolvePromise(x, resolve, reject)
            } else {
                this.successCallback.push(successCallback);
                this.failCallback.push(failCallback);
            }
        });
        return promise2
    }
}

function resolvePromise (x, resolve, reject) {
    if (x instanceof MyPromise) {
      // X是promise对象 查看promsie对象返回的结果,再根据promise对象返回的结果 决定调用resolve 还是调用reject
      x.then(resolve, reject);
    } else {
      // X是普通值 直接调用resolve 
      resolve(x);
    }
  }
module.exports = MyPromise;

 

 then方法链式调用识别Promise对象自返回

 虽然在then方法的回调函数中可以返回Promise对象,但是存在一种例外情况:在then的回调函数中,不能返回当前这个then方法它所返回的Promise对象,否则会发生Promise的循环调用。

 

 

 也就是说,在系统的Promise对象中,是可以判断出这种循环调用并捕获的。为了在我们自己的代码中实现这种功能,需要再添加一个判断:

 

 为了实现功能,我们再判断状态时,需要将当前的prosmise2当作参数传递给封装的方法中。但是在上面的代码中,需要new MyPromise执行完成后,才会存在prosmise2,所以在执行过程中时无法获取prosmise2的,所以需要将上面的代码改造成异步代码:

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

class MyPromise {
    constructor(executor) {
        executor(this.resolve, this.reject)
    }
    status = PENDING;
    value = undefined;
    reason = undefined;
    successCallback = [];
    failCallback = [];
    resolve = value => {
        if (this.status !== PENDING) return;
        this.status = FULFILLED;
        this.value = value;
        while (this.successCallback.length) this.successCallback.shift()(this.value)
    }
    reject = reason => {
        if (this.status !== PENDING) return;
        this.status = REJECTED;
        this.reason = reason;
        while (this.failCallback.length) this.failCallback.shift()(this.reason)
    }
    then(successCallback, failCallback) {
        let promise2 = new MyPromise((resolve, reject) => {
            if (this.status === FULFILLED) {
                setTimeout(() => {
                    let x = successCallback(this.value);
                    resolvePromise(promise2, x, resolve, reject)
                }, 0)
            } else if (this.status === REJECTED) {
                setTimeout(() => {
                    let x = failCallback(this.reason);
                    resolvePromise(promise2, x, resolve, reject)
                }, 0)
            } else {
                this.successCallback.push(successCallback);
                this.failCallback.push(failCallback);
            }
        });
        return promise2
    }
}

function resolvePromise(promise2, x, resolve, reject) {
    if (promise2 === x) {
        return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
    }
    if (x instanceof MyPromise) {
        x.then(resolve, reject);
    } else {
        resolve(x);
    }
}
module.exports = MyPromise;

 

 Promise捕获错误

当执行器当中的代码在执行的过程中发生错误的时候,需要让Promise的状态变为失败的状态。为了能在then方法的第二个参数处捕获到错误,需要在构造函数(调用执行器)中捕获错误。

  

 如果是then方法的回调函数在执行过程中报错了,则需要在then方法中捕获错误:

  

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

class MyPromise {
    constructor(executor) {
        try {
            executor(this.resolve, this.reject)
        } catch (e) {
            this.reject(e);
        }
    }
    status = PENDING;
    value = undefined;
    reason = undefined;
    successCallback = [];
    failCallback = [];
    resolve = value => {
        if (this.status !== PENDING) return;
        this.status = FULFILLED;
        this.value = value;
        while (this.successCallback.length) this.successCallback.shift()(this.value)
    }
    reject = reason => {
        if (this.status !== PENDING) return;
        this.status = REJECTED;
        this.reason = reason;
        while (this.failCallback.length) this.failCallback.shift()(this.reason)
    }
    then(successCallback, failCallback) {
        let promise2 = new MyPromise((resolve, reject) => {
            if (this.status === FULFILLED) {
                setTimeout(() => {
                    try {
                        let x = successCallback(this.value);
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        reject(e);
                    }
                }, 0)
            } else if (this.status === REJECTED) {
                setTimeout(() => {
                    try {
                        let x = failCallback(this.reason);
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        reject(e);
                    }
                }, 0)
            } else {
                this.successCallback.push(() => {
                    setTimeout(() => {
                        try {
                            let x = successCallback(this.value);
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (e) {
                            reject(e);
                        }
                    }, 0)
                });
                this.failCallback.push(() => {
                    setTimeout(() => {
                        try {
                            let x = failCallback(this.reason);
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (e) {
                            reject(e);
                        }
                    }, 0)
                });
            }
        });
        return promise2
    }
}

function resolvePromise(promise2, x, resolve, reject) {
    if (promise2 === x) {
        return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
    }
    if (x instanceof MyPromise) {
        x.then(resolve, reject);
    } else {
        resolve(x);
    }
}
module.exports = MyPromise;

 将then方法的参数变成可选参数

 

 在系统的Promise对象中,即使在then方法中将参数进行省略,依旧能够向下传递状态。其效果等同于:

为了在我们自己的MyPromise中实现这个功能,我们需要在then方法中判断回调函数是否存在,如果存在,则直接调用,若不存在,则添加一个上面的函数:

const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

class MyPromise {
    constructor(executor) {
        try {
            executor(this.resolve, this.reject)
        } catch (e) {
            this.reject(e);
        }
    }
    status = PENDING;
    value = undefined;
    reason = undefined;
    successCallback = [];
    failCallback = [];
    resolve = value => {
        if (this.status !== PENDING) return;
        this.status = FULFILLED;
        this.value = value;
        while (this.successCallback.length) this.successCallback.shift()(this.value)
    }
    reject = reason => {
        if (this.status !== PENDING) return;
        this.status = REJECTED;
        this.reason = reason;
        while (this.failCallback.length) this.failCallback.shift()(this.reason)
    }
    then(successCallback, failCallback) {
         // 参数可选
        successCallback = successCallback ? successCallback : value => value;
        failCallback = failCallback ? failCallback: reason => { throw reason };
        let promise2 = new MyPromise((resolve, reject) => {
            if (this.status === FULFILLED) {
                setTimeout(() => {
                    try {
                        let x = successCallback(this.value);
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        reject(e);
                    }
                }, 0)
            } else if (this.status === REJECTED) {
                setTimeout(() => {
                    try {
                        let x = failCallback(this.reason);
                        resolvePromise(promise2, x, resolve, reject)
                    } catch (e) {
                        reject(e);
                    }
                }, 0)
            } else {
                this.successCallback.push(() => {
                    setTimeout(() => {
                        try {
                            let x = successCallback(this.value);
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (e) {
                            reject(e);
                        }
                    }, 0)
                });
                this.failCallback.push(() => {
                    setTimeout(() => {
                        try {
                            let x = failCallback(this.reason);
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (e) {
                            reject(e);
                        }
                    }, 0)
                });
            }
        });
        return promise2
    }
}

function resolvePromise(promise2, x, resolve, reject) {
    if (promise2 === x) {
        return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
    }
    if (x instanceof MyPromise) {
        x.then(resolve, reject);
    } else {
        resolve(x);
    }
}
module.exports = MyPromise;

 

发表评论

0/200
227 点赞
0 评论
收藏