乐趣区

Promise的分析与模拟实现

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 ArrayOfPromises
     * @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 
     * @return: 返回一个指定结果的 promise
     */

    static resolve(data) { }
    /**
     * @author: forceddd
     * @desc: 不考虑传入 promise
     * @param 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 
     * @return: 返回一个指定结果的 promise
     */
static resolve(data) {return new MyPromise((resolve, reject) => {
            data instanceof MyPromise
                ? 
                /*res.then(data => resolve(data),
                       error => reject(error)
                        )
                    的简写    */
                data.then(resolve, reject)
                : resolve(data)
        })
    }
    /**
     * @author: forceddd
     * @desc: 不考虑传入 promise
     * @param reason 
     * @return: 返回一个失败状态的 promise 失败原因是传入的参数 
     */
    static reject(error) {return new MyPromise((_, reject) => reject(error))
    }

6.race 的实现

/**
     * @author: forceddd
     * @desc: 要判断传入的数组是否全部是 promise 对象
     * @param ArrayOfPromises
     * @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 ArrayOfPromises
     * @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) : null
                    },
                    error => reject(error) // 将 all 的返回值变为 reject 状态 
                )
            })
        })
    }
退出移动版