详解Promise的实现

1. 初级版

首先要确定promise有什么特点:

  • promise是一个类;
  • 每次new 一个promise 都需要传递一个执行器 执行器是立即执行的;
  • 执行器函数中有两个参数 resolve reject
  • 默认promise有三个状态 pending => fulfilled 表示成功了,
    pending => rejected 表示失败了;
  • 如果一旦成功了,不能变成失败;一旦失败,不能再成功了,只有当前状态是pending的时候,才能更改状态;
  • 每个promise都有一个then方法。

基于以上的特点,实现了下面一版promise的实现:

const PENDIND = 'PENDIND',
FULFILLED = 'FULFILLED',
REJECTED = 'REJECTED';

class Promise {
    constructor(executor) {
        // 创建promise,executor会立即执行
        this.value = undefined,
        this.reason = undefined;
        this.status = PENDIND
        let resolve = (value) => {
            // 确保成功了就不能失败
            if(this.status===PENDIND) {
                this.value = value;
                this.status = FULFILLED;
            }
        }
        let reject = (reason) => {
            // 确保失败了就不能成功
            if(this.status===PENDIND) {
                this.reason = reason;
                this.status = REJECTED;
            }
        }
        // 这里可能会抛出异常,抛出异常也要执行失败
        try {
            executor(resolve, reject);
        } catch (e) {
            reject(e);
        }
    }
    // then方法会判断当前的状态
    then(onfulfilled, onrejected) {
        if(this.status===FULFILLED) {
            onfulfilled(this.value);
        }
        if(this.status===REJECTED) {
            onrejected(this.reason);
        }
    }
}

// 导出当前类 commonjs定义方式
module.exports = Promise;

引入自己实现的promise来验证一下:

let Promise = require('./promise');

let p = new Promise((resolve, reject) => {
    resolve("成功了");
    // 如果抛出异常也会执行失败
    throw new Error("失败");  
})

p.then(data => {
    console.log("success", data);
}, err => {
    console.log("error", err);
})

p.then(data => {
    console.log("success", data);
}, err => {
    console.log("error", err);
})

p.then(data => {
    console.log("success", data);
}, err => {
    console.log("error", err);
})

打印值:

success 成功了
success 成功了
success 成功了

这时考虑一种情况:

let Promise = require('./promise');

let p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve("成功了");
    })
})

p.then(data => {
    console.log("success", data);
}, err => {
    console.log("error", err);
})

p.then(data => {
    console.log("success", data);
}, err => {
    console.log("error", err);
})

p.then(data => {
    console.log("success", data);
}, err => {
    console.log("error", err);
})

构造函数中有异步代码的情况,这个时候new Promise会立即执行,但是setTimeout属于宏任务,所以会先执行外部的p.then方法,但是promise现在的状态是pending,所以再用自己写的promise来执行一下,会发现什么都没有输出。
但是在规范的Promise中不是这个效果,不引入自己的promise,执行这一段代码会发现还是会输出:

success 成功了
success 成功了
success 成功了

可见存在异步的时候,会先把then中的回调函数存起来,然后promise的状态改变后,就立即执行存起来的回调函数,这是典型的发布订阅模式(发布订阅参见博文https://blog.csdn.net/zl13015214442/article/details/101382999),下面看怎么实现:

const PENDIND = 'PENDIND',
FULFILLED = 'FULFILLED',
REJECTED = 'REJECTED';

class Promise {
    constructor(executor) {
        this.value = undefined,
        this.reason = undefined;
        this.status = PENDIND;
        // 定义两个数组存放回调函数
        this.onResolvedCallbacks = [];
        this.onRejectedCallbacks = [];
        let resolve = (value) => {
            if(this.status===PENDIND) {
                this.value = value;
                this.status = FULFILLED;
                // 当调用到resolve的时候,就把数组中存放的回调函数依次执行
                this.onResolvedCallbacks.forEach(fn => fn());
            }
        }
        let reject = (reason) => {
            if(this.status===PENDIND) {
                this.reason = reason;
                this.status = REJECTED;
                // 同理,当调用到reject的时候,就把数组中存放的回调函数依次执行
                this.onRejectedCallbacks.forEach(fn => fn());
            }
        }
        try {
            executor(resolve, reject);
        } catch (e) {
            reject(e);
        }
    }
    then(onfulfilled, onrejected) {
        if(this.status===FULFILLED) {
            onfulfilled(this.value);
        }
        if(this.status===REJECTED) {
            onrejected(this.reason);
        }
        // 当存在异步的时候,先将then中的回调函数存入到设定的数组中
        if(this.status===PENDIND) {
            this.onResolvedCallbacks.push(() => {
                // todo....
                onfulfilled(this.value);
            })
            this.onRejectedCallbacks.push(() => {
                // todo....
                onrejected(this.value);
            })
        }
    }
}

module.exports = Promise;

再执行之前的例子,可以看出也输出三次成功的结果。所以总结一下,就是promise同步的话,直接执行,异步的话,发布订阅。

2. 进阶版

上面已经实现了一个简单的promise结构,下面要考虑到promise的一个特殊应用,then方法的链式调用,可以解决传统异步“回调地狱”的问题。then的链式调用大致特点如下:

  • 链式调用,如果返回的是一个普通值,会走下一个then的成功;
  • 抛出错误,就会执行then中失败的方法;
  • 如果返回的是一个promise,那么这个promise会执行,并且会采用他的状态;
  • 重点:他返回了一个新的promise来实现链式调用!

下面来看具体代码的实现:

const PENDIND = 'PENDIND',
FULFILLED = 'FULFILLED',
REJECTED = 'REJECTED';
// promise的处理函数
const resolvePromise = (promise2, x, resolve, reject) => {
    // 处理x的类型,来决定调用resolve还是reject
    // promise2自己等待自己完成,直接报错
    if(promise2 === x) {
        return reject(new TypeError(`Chaining cycle detected for promise #`));
    }
    // 判断x是不是一个普通值,暂先认为x是一个Promise来处理
    if((typeof x === 'object' && x !== null) || typeof x === 'function') {
        // x可能是一个promise,用promise的特点then方法来判断
        // 取then的过程中可能会抛出异常,报错就好了
        try {
            let then = x.then;
            if(typeof then === 'function') {
                // 即说明x是个promise,就采用这个promise的结果
                then.call(x, y => {
                    resolve(y);
                }, r => {
                    reject(r);
                })
            } else {
                // 常量直接抛出去即可
                resolve(x); 
            }
        } catch(e) {
            reject(e);
        }        
    } else {
        resolve(x);
    }

}
class Promise {
    constructor(executor) {
        this.value = undefined,
        this.reason = undefined;
        this.status = PENDIND;
        this.onResolvedCallbacks = [];
        this.onRejectedCallbacks = [];
        let resolve = (value) => {
            if(this.status===PENDIND) {
                this.value = value;
                this.status = FULFILLED;
                this.onResolvedCallbacks.forEach(fn => fn());
            }
        }
        let reject = (reason) => {
            if(this.status===PENDIND) {
                this.reason = reason;
                this.status = REJECTED;
                this.onRejectedCallbacks.forEach(fn => fn());
            }
        }
        try {
            executor(resolve, reject);
        } catch (e) {
            reject(e);
        }
    }
    then(onfulfilled, onrejected) {
        let promise2 = new Promise((resolve, reject) => {
            if(this.status===FULFILLED) {
                // 为了能够取到promise2,将其放在异步的setTimeout中
                // 当前onfulfilled和onrejected不能在当前的上下文中执行,为了确保promise2的存在
                setTimeout(() => {
                    try {
                        let x = onfulfilled(this.value);
                        // 这里还需要判断x是不是一个promise,如果是一个promise,就采用这个promise执行的结果
                        // 根据Promise A+规范这里用一个函数统一处理
                        // 这里要用x来判断promise2的成功还是失败,所以传入以下参数
                        resolvePromise(promise2, x, resolve, reject);
                    } catch(e) {
                        reject(e);
                    }
                })
            }
            if(this.status===REJECTED) {
                setTimeout(() => {
                    try {
                        let x = onrejected(this.reason);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch(e) {
                        reject(e);
                    }
                })
            }
            if(this.status===PENDIND) {
                this.onResolvedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onfulfilled(this.value);
                            resolvePromise(promise2, x, resolve, reject);
                        } catch(e) {
                            reject(e);
                        }
                    })
                })
                this.onRejectedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onrejected(this.reason);
                            resolvePromise(promise2, x, resolve, reject);
                        } catch(e) {
                            reject(e);
                        }
                    })
                })
            }
        })
        return promise2;
    }
}

module.exports = Promise;

下面来测试一下:

let Promise = require('./promise');

let p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve("成功了");
    }, 1000)
})

let promise2 = p.then(data => {    
    // 如果promise2和x引用了同一个对象,相当于自己等自己完成,会报一个类型错误
    // return promise2;
    return [1,2,34];
    // 如果返回的x是一个promise
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("又成功了");
        }, 1000)
    })
})

promise2.then(data => {
    console.log("success", data);
}, err => {
    console.log("error", err);
})

打印的结果也是理想的,上面的实例包含了多中情况,包括返回的是一个自身的promise会报错then中返回的是一个promise实例等等,都符合promise A+规范。
下面考虑一种特殊的情况,那就是promise成功之后,再resolve一个新的promise

// let Promise = require('./promise');
let p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve("成功了");
    }, 1000)
})

let promise2 = p.then(data => {    
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(new Promise((resolve, reject) => {
                setTimeout(() => {
                    resolve("又成功了");
                }, 1000)
            }));
        }, 1000)
    })
})

promise2.then(data => {
    console.log("success", data);
}, err => {
    console.log("error", err);
})

标准的Promise返回的结果是成功执行resolvepromise的结果:

success 又成功了

而显然我们自己实现的promise还做不到这一点,需要在原本实现的promise基础上对resolvePromise方法进行改进,就是要考虑到该方法中的y有可能还是个promise,所以再调用resolvePromisey进行判断并处理就可以了,其实就是不停地递归的一个过程,知道返回的y是个常量,就输出:

const resolvePromise = (promise2, x, resolve, reject) => {
    if(promise2 === x) {
        return reject(new TypeError(`Chaining cycle detected for promise #`));
    }
    if((typeof x === 'object' && x !== null) || typeof x === 'function') {
        try {
            let then = x.then;
            if(typeof then === 'function') {
                then.call(x, y => {
                    // y有可能还是个promise,所以需要继续判断
                    resolvePromise(promise2, y, resolve, reject);
                }, r => {
                    reject(r);
                })
            } else {
                resolve(x); 
            }
        } catch(e) {
            reject(e);
        }        
    } else {
        resolve(x);
    }
}

再尝试之前的例子,可以输出promise都执行完之后的结果了。
在这个基础上再考虑一种情况,如果返回的是一个新的和之前的promise没有任何关系的promise,那我们实现的promise就不能保证他成功之后不会继续调用失败,这里需要加入判断条件called,调用过成功就不能调用失败了,防止重复调用:

const resolvePromise = (promise2, x, resolve, reject) => {
    if(promise2 === x) {
        return reject(new TypeError(`Chaining cycle detected for promise #`));
    }
    if((typeof x === 'object' && x !== null) || typeof x === 'function') {
    	let called;
        try {
            let then = x.then;
            // 默认没有调用成功和失败
            if(typeof then === 'function') {
                then.call(x, y => {
                    // 一旦成功,就让called为true
                    // 防止多次调用
                    if(called) return;
                    called = true;
                    resolvePromise(promise2, y, resolve, reject);
                }, r => {
                    if(called) return;
                    called = true;
                    reject(r);
                })
            } else {
                resolve(x); 
            }
        } catch(e) {
            if(called) return;
            called = true;
            reject(e);
        }        
    } else {
        resolve(x);
    }
}

以下是最终版的Promise实现:

const PENDIND = 'PENDIND',
FULFILLED = 'FULFILLED',
REJECTED = 'REJECTED';
const resolvePromise = (promise2, x, resolve, reject) => {
    if(promise2 === x) {
        return reject(new TypeError(`Chaining cycle detected for promise #`));
    }
    if((typeof x === 'object' && x !== null) || typeof x === 'function') {
        let called;        
        try {
            let then = x.then;
            if(typeof then === 'function') {
                then.call(x, y => {
                    if(called) return;
                    called = true;
                    resolvePromise(promise2, y, resolve, reject);
                }, r => {
                    if(called) return;
                    called = true;
                    reject(r);
                })
            } else {
                resolve(x); 
            }
        } catch(e) {
            if(called) return;
            called = true;
            reject(e);
        }        
    } else {
        resolve(x);
    }
}

class Promise {
    constructor(executor) {
        this.value = undefined,
        this.reason = undefined;
        this.status = PENDIND;
        this.onResolvedCallbacks = [];
        this.onRejectedCallbacks = [];
        let resolve = (value) => {
            if(this.status===PENDIND) {
                this.value = value;
                this.status = FULFILLED;
                this.onResolvedCallbacks.forEach(fn => fn());
            }
        }
        let reject = (reason) => {
            if(this.status===PENDIND) {
                this.reason = reason;
                this.status = REJECTED;
                this.onRejectedCallbacks.forEach(fn => fn());
            }
        }
        try {
            executor(resolve, reject);
        } catch (e) {
            reject(e);
        }
    }
    then(onfulfilled, onrejected) {
        let promise2 = new Promise((resolve, reject) => {
            if(this.status===FULFILLED) {
                setTimeout(() => {
                    try {
                        let x = onfulfilled(this.value);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch(e) {
                        reject(e);
                    }
                })
            }
            if(this.status===REJECTED) {
                setTimeout(() => {
                    try {
                        let x = onrejected(this.reason);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch(e) {
                        reject(e);
                    }
                })
            }
            if(this.status===PENDIND) {
                this.onResolvedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onfulfilled(this.value);
                            resolvePromise(promise2, x, resolve, reject);
                        } catch(e) {
                            reject(e);
                        }
                    })
                })
                this.onRejectedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onrejected(this.reason);
                            resolvePromise(promise2, x, resolve, reject);
                        } catch(e) {
                            reject(e);
                        }
                    })
                })
            }
        })
        return promise2;
    }
}

module.exports = Promise;

你可能感兴趣的:(JavaScript)