Promise原理解析-手写Promise

114 阅读4分钟

1. Promise核心逻辑实现

  1. Promise是一个类,需要在构建函数中传入一个execute的执行函数,并且会立即执行的

  2. Promise会提供俩个方法并在execute暴露出来,用以改变Promise的状态。并且可以传入参数,resolve 的参数表示成功的值,rejected的参数表示失败的原因。

  3. Promise拥有三种状态(一旦确定就不能更改)

  4. PENDING 等待状态

  5. FULFILLED 成功状态

  6. REJECTED  失败状态

  7. Promise上有then函数,用于判断promise 的状态,根据状态调用不同的回调函数。把成功的值或者失败的原因传递下去

因此我们封装一下状态:

const STATUS = {    
    PENDING: Symbol('pending'),    
    FULFILLED: Symbol('fulfilled'),    
    REJECTED: Symbol('rejected')
}

那么Promise的实现如下:

class MPromise {  
    constructor(executor) {    
        if (typeof executor === "function") {      
            executor(this.resolve, this.rejected);    
        }  
    }  
    status = STATUS.PENDING;  
    value = undefined;  
    reason = undefined;  
    resolve = (value) => {    
        // 如果状态已经改变,禁止再次改变    
        if (this.status !== STATUS.PENDING) return;    
        // 将状态修改成fulfilled  成功状态    
        this.status = STATUS.FULFILLED;    
        this.value = value;  
    };  
    rejected = (reason) => {    
        // 将状态修改成rejected  失败状态    
        if (this.status !== STATUS.PENDING) return;    
        this.status = STATUS.REJECTED;    
        this.reason = reason;  
    };  
    then(successCallBack, errCallBack) {    
        // 判断promise 的状态,根据状态调用不同的回调函数    
        if (this.status === STATUS.FULFILLED) {      
            successCallBack(this.value);    
        } else if (this.status === STATUS.REJECTED) {      
            errCallBack(this.reason);    
        }  
    }
}

至此,我们实现了Promise的核心逻辑。但是这也只是最基本的功能,下面我们需要完善我们的代码。

2. 异步逻辑处理

当执行函数有异步逻辑时,当前的代码无法满足我们的需求。因此我们需要先把回调函数存储起来,在异步结束之后去执行回调函数。

class MPromise {  
    constructor(executor) {    
        if (typeof executor === "function") {      
            executor(this.resolve, this.rejected);    
        }  
    }  
    status = STATUS.PENDING;  
    value = undefined;  
    reason = undefined;  
    successCallBack = undefined;  
    errCallBack = undefined;  
    resolve = (value) => {    
        // 如果状态已经改变,禁止再次改变    
        if (this.status !== STATUS.PENDING) return;    
        // 将状态修改成fulfilled  成功状态    
        this.status = STATUS.FULFILLED;    
        this.value = value;    
        // 如果有成功回调就执行    
        this.successCallBack && this.successCallBack(this.value);  
    };  
    rejected = (reason) => {    
        // 将状态修改成rejected  失败状态    
        if (this.status !== STATUS.PENDING) return;    
        this.status = STATUS.REJECTED;    
        this.reason = reason;    
        this.errCallBack && this.errCallBack(this.reason);  
    };    
    then(successCallBack, errCallBack) {    
        // 判断promise 的状态,根据状态调用不同的回调函数    
        if (this.status === STATUS.FULFILLED) {      
            successCallBack(this.value);    
        } else if (this.status === STATUS.REJECTED) {      
            errCallBack(this.reason);    
        } else {      
            // 将回调函数存储起来      
            this.successCallBack = successCallBack;      
            this.errCallBack = errCallBack;    
        }  
    }
}

3. then函数的多次调用以及链式调用

当我们then函数有多次调用的时候,当前的then函数只会执行一次,所以我们用数组来存储回调函数,循环调用。

// errCallBack = undefined
errCallBackList = [];
// this.errCallBack && this.errCallBack(this.reason);
while (this.errCallBackList.length > 0) this.errCallBackList.shift()();

// this.errCallBack = errCallBack;
this.errCallBackList.push(() => {  errCallBack(this.reason);});

then函数的链式调用,说明其返回的是一个新的Promise对象,但是如果then内部的回调函数执行结果是一个值可以直接传递下去,但如果结果依然是一个promise那就把这个promise的结果resolve出去。

因此我们需要判断返回值的类型:

function resolvePromise(result, resolve, reject) {  
    if (result instanceof MPromise) {    
        result.then(resolve, reject);  
    } else {    
        resolve(result);  
    }
}

then函数代码:

then(successCallBack, errCallBack) {    
    let nPromise = new MPromise((resolve, rejected) => {      
        // 判断promise 的状态,根据状态调用不同的回调函数      
        if (this.status === STATUS.FULFILLED) {        
            let res = successCallBack(this.value);        
            resolvePromise(res, resolve, rejected);      
        } else if (this.status === STATUS.REJECTED) {           
            let res = errCallBack(this.reason);        
            resolvePromise(res, resolve, rejected);      
        } else {        
            // 将回调函数存储起来        
            this.successCallBackList.push(() => {          
                let res = successCallBack(this.value);          
                resolvePromise(res, resolve, rejected);        
            });        
            this.errCallBackList.push(() => {          
                let res = errCallBack(this.reason);          
                resolvePromise(res, resolve, rejected);        
            });      
        }    
       });    
    return nPromise;  
}

promise对象循环调用问题:(promise会抛出一个错误:Uncaught (in promise) TypeError: Chaining cycle detected for promise #<Promise>

let p = new Promise((resolve, rejected) => {    
    resolve(12);
});
let p2 = p.then((res) => {  return p2;});

因此我们需要判断我们返回的Promise是否存在这种问题,我们优化了resolvePromise方法,如下:

function resolvePromise(result,nPromise, resolve, reject) {  
    if(result === nPromise ){    
        reject(new TypeError("Uncaught (in promise) TypeError: Chaining cycle detected for promise #<Promise>"))  
    }  if (result instanceof MPromise) {    
        result.then(resolve, reject);  
    } else {    
        resolve(result);  
    }
}

then函数中的参数都是可选参数,因此我们需要将参数改成可选的,即添加默认值:

successCallBack =  typeof successCallBack === "function" ? successCallBack : (value) => this.value;
errCallBack =  typeof errCallBack === "function" ? errCallBack : (reason) => this.reason;

4. Promise中的异常处理

1. 执行器中的异常处理

constructor(executor) {    
    if (typeof executor === "function") {      
        try {        
            executor(this.resolve, this.rejected);      
        } catch (error) {        
            this.rejected(error)      
        }    
    }  
}

2. then函数中的异常处理

我们封装一个函数统一处理异常

const handle = (callback,value) => {  
    setTimeout(() => {    
        try {      
            let res = callback(value);      
            resolvePromise(res, nPromise, resolve, rejected);    
        } catch (error) {      
            reject(error);    
        }  
    }, 0);
};

我们整体的then函数就完成了:

then(successCallBack, errCallBack) {  
    successCallBack =    typeof successCallBack === "function"? successCallBack : (value) => this.value;  
    errCallBack =    typeof errCallBack === "function" ? errCallBack : (reason) => this.reason;  
    let nPromise = new MPromise((resolve, rejected) => {    
        const handle = (callback,value) => {      
            setTimeout(() => {        
                try {          
                    let res = callback(value);          
                    resolvePromise(res, nPromise, resolve, rejected);        
                } catch (error) {          
                    reject(error);        
                }      
            }, 0);    
        };    
        // 判断promise 的状态,根据状态调用不同的回调函数    
        if (this.status === STATUS.FULFILLED) {      
            handle(successCallBack,this.value)    
        } else if (this.status === STATUS.REJECTED) {      
            handle(errCallBack,this.reason)    
        } else {      
            // 将回调函数存储起来      
            this.successCallBackList.push(() => {        
                handle(successCallBack,this.value)      
            });      
            this.errCallBackList.push(() => {        
                handle(errCallBack,this.reason)      
            });    
        }  
     });  
    return nPromise;
}

5. catch函数的实现

catch方法其实就是也是一个then方法,只是没有成功回调:

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

至此,除了一些的静态方法,我们Promise基本完善了。