手撕Promise

Promise构造函数

Promise构造函数需要传的参数就是一个executor构造器函数。executor里需要传resolve和reject函数,用于改变Promise实例的状态

  • 功能:立即执行executor代码,并且传递出两个函数用于改变Promise实例状态
  • 参数:resolve函数和reject函数
  • 返回值:无
//定义状态
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';

function Promise(executor){
    const that = this; //保证this指向实例
    this.status = PENDING; //初始化状态
    this.data = undefined; //保存成功或失败回调的值
    this.callbacks = []; //保存回调队列,用{onResolve,onReject}对象存储指定的回调函数队列
 	//更改实例状态为成功的函数
    function resolve(value){
        //判定当前状态是否为pending,若不是则不允许第二次更改状态
        if(that.status !== PENDING) return ;
        //将实例状态更改为fulfilled并保存传入的value
        that.status = FULFILLED;
        that.data = value;
        //检查回调队列有没有回调函数需要调用
        if (that.callbacks.length > 0) {
            //将成功回调推入微队列异步执行
            queueMicrotask(() => {
                that.callbacks.forEach(cb => cb.onResolve(that.data));
            });
        }
    }
 	//更改实例状态为失败的函数
    function reject(reason){
        //判定当前状态是否为pending,若不是则不允许第二次更改状态
        if(that.status !== PENDING) return ;
        //将实例状态更改为rejected并保存传入的reason
        that.status = REJECTED;
        that.data = reason;
        //检查回调队列有没有回调函数需要调用
        if (that.callbacks.length > 0) {
            //将失败回调推入微队列异步执行
            queueMicrotask(() => {
                that.callbacks.forEach(cb => cb.onResolve(that.data));
            });
        }
    }
    //同步执行executor,并向外传递出改变实例状态的函数
    executor(resolve,reject);
}
复制代码

Promise.prototype.then()

  • 功能:给实例对象指定成功和失败的回调
  • 参数:成功的回调函数和失败的回调函数
  • 返回值:return一个新的Promise实例
  • 特性:return的Promise实例状态根据传入回调函数的执行结果决定
  • return的Promise状态影响因素
    1. 回调函数执行抛出异常,return的新Promise状态为rejected,reason为该异常
    2. 回调函数返回非Promise值,return的新Promise状态为fulfilled,value为回调函数的返回值
    3. 回调函数返回Promise值,return的新Promise状态和值都跟随返回的Promise
Promise.prototype.then = function(onResolve,onReject){
    const that = this;
    //判定传入的成功回调是否为函数,若不是则强制改为函数
    onResolve = typeof onResolve === 'function' ? onResolve : value => value ;
    //判定传入的失败回调是否为函数,若不是则原样抛出异常,实现异常穿透
    onReject = typeof onReject === 'function' ? onReject : reason => {throw reason};
    return new Promise((resolve,reject)=>{
        //定义handle函数处理所有能影响return的Promise状态的情况
        function handle(callback){
            	//1.若回调函数执行抛出异常则捕获异常
                try{
                    const res = callback(that.data); //接收成功回调的返回值
                    //判断返回值类型并处理
                    if(res instanceof Promise){
                        //3.根据res的状态决定return的Promise状态
                        res.then(resolve,reject);
                    }else{ //2.非Promise返回值直接返回
                        resolve(res);
                    }
                }catch(error){
                    reject(error);
                }
        }
        //若此时是先指定了回调函数,后面改变的状态则将回调放入回调队列
        if(that.status === PENDING){
            that.callbacks.push(
                {
                    onResolve(value){
                        handle(onResolve);
                    },
                    onReject(reason){
					  handle(onReject);
                    }
                }
            )
        }else if(that.status === FULFILLED){ //若先改变了状态,则直接将回调函数推入微队列执行
            queueMicrotask(()=>{
                handle(onResolve);   
            })
        }else{
            queueMicrotask(()=>{
                handle(onReject); 
            })
				
        }  
    })
}
复制代码

Promise.prototype.catch()

  • 功能:给实例对象指定失败的回调
  • 参数:失败的回调函数
  • 返回值:return一个新的Promise实例
  • 特性:return的Promise实例状态根据传入回调函数的执行结果决定
  • return的Promise状态影响因素
    1. 回调函数执行抛出异常,return的新Promise状态为rejected,reason为该异常
    2. 回调函数返回非Promise值,return的新Promise状态为fulfilled,value为回调函数的返回值
    3. 回调函数返回Promise值,return的新Promise状态和值都跟随返回的Promise
Promise.prototype.catch = function(onReject){
    return this.then(undefined,onReject);
}
复制代码

Promise.resolve()

  • 功能:根据传入的value返回一个已经改变了状态的Promise
  • 参数:value
  • 返回值:return一个新的Promise实例
  • 特性:return的Promise实例状态根据传入value决定
  • return的Promise状态影响因素
    1. value为一个抛出的异常,return的新Promise状态为rejected,reason为该异常
    2. value是一个非Promise,return的新Promise状态为fulfilled,value为回调函数的返回值
    3. value是一个Promise,return的新Promise状态和值都跟随返回的Promise
//仅需根据then中handle函数的逻辑重写一遍即可
Promise.resolve = function(value){
    return new Promise((resolve,reject)=>{
        if(value instanceof Promise){
            value.then(resolve,reject);
        }else if(value instanceof Error){
            reject(value.message);
        }else{
            resolve(value);
        }
    })
}
复制代码

Promise.reject()

  • 功能:根据传入的value返回一个已经改变了状态的Promise
  • 参数:value
  • 返回值:return一个新的rejected状态的Promise实例
  • 特性:return的Promise实例状态一定是rejected
Promise.reject = function(reason){
    return new Promise((resolve,reject)=>{
        if(reason instanceof Error){
            reject(reason.message);
        }else if(reason instanceof Promise){
            reject(reason.data);
        }else{
           	reject(reason);
        }
    })
}
复制代码

Promise.all()

  • 功能:根据传入的Promise数组,若该数组的Promise最后全都成功则返回一个fulfilled的Promise,否则返回一个rejected的Promise
  • 参数:Promise数组
  • 返回值:return一个Promise实例
  • 特性:return的Promise实例状态根据传入的Promise数组决定
Promise.all = function(promiseArr){
    const values = [];//定义一个values保存成功Promise的value的值
    let count = 0; //记录有几个Promise成功
    return new Promise((resolve,reject)=>{
        //遍历数组所有元素
        promiseArr.forEach((p,index)=>{
           Promise.resolve(p).then( //用Promise.resolve包裹,处理数组中有非Promise值的情况
                value => {
                    ++count;
                    values[index] = value;
                    if(count === promiseArr.length) resolve(values);
                },
                reason => reject(reason))
        });
    })
}
复制代码

Promise.race()

  • 功能:根据传入的Promise数组,return的Promise状态跟随第一个完成的Promise
  • 参数:Promise数组
  • 返回值:return一个Promise实例
  • 特性:return的Promise实例状态跟随第一个完成的Promise
Promise.race = function(promiseArr){
    return new Promise((resolve,reject)=>{
        promiseArr.forEach((p)=>{
            Promise.resolve(p).then( //用Promise.resolve包裹,处理数组中有非Promise值的情况
                value => resolve(value),
                reason => reject(reason)
            )
        })
    })
}
复制代码

完整代码

ES5版

(function (w) {
    const PENDING = 'pending';
    const FULFILLED = 'fulfilled';
    const REJECTED = 'rejected';
    function Promise(executor) {
        const that = this;
        that.status = PENDING;
        that.data = undefined;
        that.callbacks = [];
        function resolve(value) {
            if (that.status !== PENDING) return;
            that.status = FULFILLED;
            that.data = value;
            if (that.callbacks.length > 0) {
                queueMicrotask(() => {
                    that.callbacks.forEach(cb => cb.onResolve(that.data));
                });
            }
        };
        function reject(reason) {
            if (that.status !== PENDING) return;
            that.status = REJECTED;
            that.data = reason;
            if (that.callbacks.length > 0) {
                queueMicrotask(() => {
                    that.callbacks.forEach(cb => cb.onReject(that.data));
                });
            }
        };
        executor(resolve, reject);
    };

    Promise.prototype.then = function (onResolve, onReject) {
        const that = this;
        onResolve = typeof onResolve === 'function' ? onResolve : value => value;
        onReject = typeof onReject === 'function' ? onReject : reason => { throw reason };
        return new Promise((resolve, reject) => {
            function handle(callback) {
                try {
                    const res = callback(that.data);
                    if (res instanceof Promise) {
                        res.then(resolve, reject);
                    } else { 
                        resolve(res);
                    }
                } catch (error) {
                    reject(error);
                }
            }
            if (that.status === PENDING) {
                that.callbacks.push(
                    {
                        onResolve(value) {
                            handle(onResolve);
                        },
                        onReject(reason) {
                            handle(onReject);
                        }
                    }
                )
            } else if (that.status === FULFILLED) {
                queueMicrotask(() => {
                    handle(onResolve);
                })
            } else {
                queueMicrotask(() => {
                    handle(onReject);
                })
            };
        });
    };

    Promise.prototype.catch = function (onReject) {
        return this.then(undefined, onReject);
    };

    Promise.resolve = function (value) {
        return new Promise((resolve, reject) => {
            if (value instanceof Promise) {
                value.then(resolve, reject);
            } else if (value instanceof Error) {
                reject(value.message);
            } else {
                resolve(value);
            }
        })
    };

    Promise.reject = function (reason) {
        return new Promise((resolve, reject) => {
            if (reason instanceof Error) {
                reject(reason.message);
            } else if (reason instanceof Promise) {
                reject(reason.data);
            } else {
                reject(reason);
            }
        })
    };

    Promise.all = function (promiseArr) {
        const values = [];
        let count = 0;
        return new Promise((resolve, reject) => {
            promiseArr.forEach((p, index) => {
                Promise.resolve(p).then(
                    value => {
                        ++count;
                        values[index] = value;
                        if (count === promiseArr.length) resolve(values);
                    },
                    reason => reject(reason))
            });
        })
    };

    Promise.race = function (promiseArr) {
        return new Promise((resolve, reject) => {
            promiseArr.forEach((p) => {
                Promise.resolve(p).then(
                    value => resolve(value),
                    reason => reject(reason)
                )
            })
        })
    };

    w.Promise = Promise;
})(window)
复制代码

ES6版

(function (w) {
    const PENDING = 'pending';
    const FULFILLED = 'fulfilled';
    const REJECTED = 'rejected';
    class Promise {
        constructor(executor) {
            const that = this;
            that.status = PENDING;
            that.data = undefined;
            that.callbacks = [];
            function resolve(value) {
                if (that.status !== PENDING) return;
                that.status = FULFILLED;
                that.data = value;
                if (that.callbacks.length > 0) {
                    queueMicrotask(() => {
                        that.callbacks.forEach(cb => cb.onResolve(that.data));
                    });
                }
            }
            function reject(reason) {
                if (that.status !== PENDING) return;
                that.status = REJECTED;
                that.data = reason;
                if (that.callbacks.length > 0) {
                    queueMicrotask(() => {
                        that.callbacks.forEach(cb => cb.onReject(that.data));
                    });
                }
            }
            executor(resolve, reject);
        };
        then(onResolve, onReject) {
            const that = this;
            onResolve = typeof onResolve === 'function' ? onResolve : value => value;
            onReject = typeof onReject === 'function' ? onReject : reason => { throw reason };
            return new Promise((resolve, reject) => {
                function handle(callback) {
                    try {
                        const res = callback(that.data);
                        if (res instanceof Promise) {
                            res.then(resolve, reject);
                        } else {
                            resolve(res);
                        }
                    } catch (error) {
                        reject(error);
                    }
                }
                if (that.status === PENDING) {
                    that.callbacks.push(
                        {
                            onResolve(value) {
                                handle(onResolve);
                            },
                            onReject(reason) {
                                handle(onReject);
                            }
                        }
                    )
                } else if (that.status === FULFILLED) {
                    queueMicrotask(() => {
                        handle(onResolve);
                    })
                } else {
                    queueMicrotask(() => {
                        handle(onReject);
                    })
                };
            });
        };
        catch(onReject) {
            return this.then(undefined, onReject);
        };
        static resolve(value) {
            return new Promise((resolve, reject) => {
                if (value instanceof Promise) {
                    value.then(resolve, reject);
                } else if (value instanceof Error) {
                    reject(value.message);
                } else {
                    resolve(value);
                }
            })
        };
        static reject(reason) {
            return new Promise((resolve, reject) => {
                if (reason instanceof Error) {
                    reject(reason.message);
                } else if (reason instanceof Promise) {
                    reject(reason.data);
                } else {
                    reject(reason);
                }
            })
        };
        static all(promiseArr) {
            const values = [];
            let count = 0;
            return new Promise((resolve, reject) => {
                promiseArr.forEach((p, index) => {
                    Promise.resolve(p).then(
                        value => {
                            ++count;
                            values[index] = value;
                            if (count === promiseArr.length) resolve(values);
                        },
                        reason => reject(reason))
                });
            })
        };
        static race(promiseArr) {
            return new Promise((resolve, reject) => {
                promiseArr.forEach((p) => {
                    Promise.resolve(p).then(
                        value => resolve(value),
                        reason => reject(reason)
                    )
                })
            })
        };
    }
    w.Promise = Promise;
})(window)
复制代码

Guess you like

Origin juejin.im/post/7032564107899322381