# 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 状态影响因素
    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{
            resolve(value);
        }
    })
}

# Promise.reject()

  • 功能:根据传入的 value 返回一个已经改变了状态的 Promise
  • 参数:reason
  • 返回值: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 {
                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 {
                    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)