1

1.了解Promise

众所周知,Promise是JS中异步编程的一种解决方案。
在模拟实现Promise之前,首先需要了解Promise的特性。

1.一个promise(Promise的实例),有三种状态,初始时的等待态pending,成功时的成功态resolved以及失败态rejected。promise的状态只能被改变一次,只能从pending成为resolved或者rejected,改变不可逆。
2.promise改变状态的方式有三种,当调用resolve时,由pending变为resolved,当调用reject,或者执行器函数内部抛出异常时,由pending变为rejected。
3.对同一个promise指定了多个回调函数时,当promise变为对应状态时,对应的回调函数全部都会执行。
4.p.then() 返回的Promise的结果状态怎么确定?
由then指定的回调函数的结果决定
①如果抛出异常,新的promise变成rejected
②如果回调函数返回的是不是Promise的任意值string,undefined等等,新的promise变为fulfilled,数据是返回的值
③如果回调函数返回的是一个promise对象。返回值promise对象的结果和状态就是新的promise的结果和状态
5.promise异常穿透:在之前的then中只写了一个成功的回调,没有写失败的回调 相当于隐式的写了 err=>{throw err} 所以会穿透
①当使用promise的then链式调用时,可以在最后指定失败的回调
②前面的任何操作出现了异常都会传到最后的失败的回调函数中处理
6.中断promise链:当使用promise链式调用时,在中间中断,不再调用后面的回调函数
方法:在需要中断处 返回一个pending状态的promise return new Promise(()={})

2.Promise的模拟实现

实现Promise中的then、catch、all、race、resolve、reject方法。
使用的时ES6语法。

1.MyPromise整体结构

class MyPromise {
    //构造函数 参数是执行器函数
    constructor(executor) {
       
    }
    //指定成功和失败的回调函数,返回一个新的promise
    then(resolvedCallBack, rejectedCallBack = error => { throw error }) {
    }
    //返回一个新的promise
    catch(rejectedCallBack) {
    }
    /**
     * @author: forceddd
     * @desc: 要判断传入的数组是否全部是promise对象
     * @param {Promise[]} promises
     * @return: 返回最先完成的promise任务结果和状态 无论成功或者失败
     */
    static race(promises) {
    }
    /**
     * @author: forceddd
     * @desc: 当所有promise都成功时,返回一个成功态的promise 数据是所有promise成功数据组成的数组 否则返回一个失败态的promise
     * @param ArrayOfPromises
     * @return: promise
     */
    static all(promises) {
       
    }
    /**
     * @author: forceddd
     * @desc: 传入非promise 返回一个成功态的promise data是该参数
     * 传入promise 返回值的状态和结果由参数promise的状态和结果决定
     * @param {any} data 
     * @return: 返回一个指定结果的promise
     */

    static resolve(data) {

    }
    /**
     * @author: forceddd
     * @desc: 不考虑传入promise
     * @param {any} error 拒绝的reason 
     * @return: 返回一个失败状态的promise 失败原因是传入的参数 
     */
    static reject(error) {
    }

}

2.构造器的实现

//构造函数 参数是执行器函数(执行器函数是同步执行的)
    constructor(executor) {
        this.status = 'pending';//当前实例的状态
        this.data = undefined;//用于存储完成后的数据
        this.callBacks = [];//用于存储promise完成之前,即状态仍为pending时,便指定的回调函数  [{resolveCb,rejectCb}] 
        //箭头函数让this指向构造函数中的this 即实例 否则resolve和reject中的this会指向window
        //声明两个用于改变promise状态的函数,并且要作为参数传入执行器函数executor中
        const resolve = data => {
            //status只能被修改一次 判断当前是否是初始值pending
            if (this.status !== 'pending') return;

            //改变promise状态为成功态
            this.status = 'resolved';
            //存储成功值
            this.data = data;

            //如果此时有的回调函数已经被指定了 应当立即--异步地--执行这些回调函数
            //通过setTimeout将成功的回调函数放入异步队列
            if (this.callBacks.length) {
                setTimeout(() => {
                    this.callBacks.forEach(cbObj => cbObj.resolvedCallBack(data))
                })
            }
        }
        const reject = error => {
            //status只能被修改一次 判断当前是否是初始值pending
            if (this.status !== 'pending') return;

            //改变promise状态为失败态
            this.status = 'rejected';
            //存储失败信息
            this.data = error;
            //如果此时有的回调函数已经被指定了 应当立即--异步--执行这些回调函数
            //通过setTimeout将失败地回调函数放入异步队列
            if (this.callBacks.length) {
                setTimeout(() => {
                    this.callBacks.forEach(cbObj => cbObj.rejectedCallBack(error))
                })
            }
        }
        // 同步执行executor try catch用于捕获executor抛出的异常
        try {
            executor(resolve, reject)
        } catch (error) {
            //如果抛出了异常,promise要变为rejected
            reject(error)
        }
    }

3.then的实现

//指定成功和失败的回调函数,返回一个新的promise
    //给rejectedCallBack默认值 实现异常穿透处理 即不传入rejectedCallBack,
    //但又是失败态时,会执行默认函数,抛出异常,将then返回值promise转为失败态
    then(resolvedCallBack, rejectedCallBack = error => { throw error }) {
        //因为在catch中 resolvedCallBack我会传入一个null 所以此处增加一个判断条件
        resolvedCallBack = typeof resolvedCallBack === 'function' ? resolvedCallBack : _ => _;
        //then的返回值是一个promise
        return new MyPromise((resolve, reject) => {
            /**
             * @desc: 处理then的返回值 
             * @ 1.如果执行回调函数时抛出了异常 返回一个失败的promise error是异常信息
             * @ 2.如果回调函数返回值不是promise对象 ,返回一个成功的promise data是回调函数返回值
             * @ 3.如果回调函数的返回值是一个promise  这个promise的状态和结果作为返回值promise的状态和结果                 
             * @param: cb
             * @return: 
             */
            const handleReturn = cb => {
                let res;//接收成功或者失败的回调函数的返回值
                try {
                    res = cb(this.data);
                    res instanceof MyPromise
                        //3.如果回调函数的返回值是一个promise  这个promise的状态和结果作为返回值promise的状态和结果
                        ? res.then(
                            data => resolve(data),
                            error => reject(error)
                        )
                        //简写形式 成功时 将resolve作为回调函数resolvedCallBack传入 内部会执行 resolvedCallBack(this.data),
                        //和传入一个data => resolve(data)效果相同
                        // res.then(resolve, reject)
                        //2.如果回调函数返回值不是promise对象 ,返回一个成功的promise data是回调函数返回值
                        : resolve(res)
                } catch (error) {
                    //1.如果执行回调函数时抛出了异常 返回一个失败的promise error是异常信息 
                    reject(error)
                }
            }
            if (this.status === 'pending') {
                //此时实例仍然时pending态 回调函数已经被声明了 将指定的回调函数存储到callBacks中
                //此时同样要根据回调函数返回值来判断then的返回值 所以不能直接像下面一样存储回调函数,需要外包一层函数,以便对返回值进行判断
                // this.callBacks.push({ resolvedCallBack, rejectedCallBack })
                this.callBacks.push({
                    resolvedCallBack: _ => handleReturn(resolvedCallBack),
                    rejectedCallBack:_ => handleReturn(rejectedCallBack)
                })
                /* 在状态改变之后 指定的回调函数 会立即--异步--执行 */
            } else if (this.status === 'resolved') {
                setTimeout(() => handleReturn(resolvedCallBack));
            } else {
                setTimeout(() => handleReturn(rejectedCallBack));
            }
        })
    }

4.catch的实现

    //返回一个新的promise catch是then的一个语法糖
    catch(rejectedCallBack) {
        return this.then(null, rejectedCallBack)
    }

5.resolve和reject的实现

/**
     * @author: forceddd
     * @desc: 传入非promise 返回一个成功态的promise data是该参数
     * 传入promise 返回这个promise
     * @param {any} data
     * @return: 返回一个指定结果的promise
     */
    static resolve(data) {
        return data instanceof MyPromise
            ? data
            : new MyPromise((resolve) => {
                  resolve(data);
              });
    }
    /**
     * @author: forceddd
     * @desc: 不考虑传入promise
     * @param {any} reason
     * @return: 返回一个失败状态的promise 失败原因是传入的参数
     */
    static reject(error) {
        return new MyPromise((_, reject) => reject(error));
    }

6.race的实现

/**
     * @author: forceddd
     * @desc: 要判断传入的数组是否全部是promise对象
     * @param {Promise[]} promises
     * @return: 返回最先完成的promise任务结果 无论成功或者失败
     */
    static race(promises) {
        //返回一个promise
        return new MyPromise((resolve, reject) => {
            promises.forEach(promise => {
                //判断元素是不是promise 如果不是,通过resolve函数转换成promise
                promise = promise instanceof MyPromise ? promise : MyPromise.resolve(promise);
                //将最先完成的promise状态和结果 作为race返回值的状态和结果 
                promise.then(resolve, reject)
            })
        })
    }

7.all的实现

 /**
     * @author: forceddd
     * @desc: 当所有promise都成功时,返回一个成功态的promise 数据是所有promise成功数据组成的数组 否则返回一个失败态的promise
     * @param {Promise[]} promises
     * @return: promise
     */
    static all(promises) {
        let resolveCount = 0;//记录执行then中成功回调函数的次数 每执行一次then的回调函数说明有个一个promise已经有了结果
        const results = new Array(promises.length)//创建一个长度和promises相同的数组,用来存储成功的data
        return new MyPromise((resolve, reject) => {
            promises.forEach((promise, index) => {
                //将不是promise的元素转换成promise
                promise = promise instanceof MyPromise ? promise : MyPromise.resolve(promise);
                promise.then(
                    data => {
                        results[index] = data;
                        resolveCount++;
                        resolveCount === promises.length && resolve(results); 
                    },
                    reject //将all的返回值变为reject状态 
                )
            })
        })
    }

forceddd
271 声望912 粉丝

一名前端爱好者。