美文网首页
手写Promise实现

手写Promise实现

作者: 风雅欢乐 | 来源:发表于2020-05-02 20:21 被阅读0次

    初学ES 6的Promise之后, 感觉这个新的异步处理模型有点绕, 为了加深理解, 所以尝试手写Promise的实现, 尽可能的实现原生的Promise的方法和效果.

    在写代码之前, 先对Promise相关的知识和使用方式进行整理.

    Promise的状态

    Promise作为ES 6标准中提出的异步处理模型, 它分为两个阶段, 三种状态.

    • 未决阶段 -- 对应状态为pending
    • 已决阶段 -- 对应状态为resolved或rejected
      Promise只能从未决阶段, 发展到已决阶段, 并且状态一旦发生改变, 就固定下来不能再变化.

    Promise的使用

    Promise是一个构造函数, 它有一个参数, 这个参数是一个函数, 表示在未决阶段需要执行的代码. 这个函数提供了两个参数resolve和reject, 这两个参数也是函数, 分别表示将未决阶段推向已决阶段的resolved状态或rejected状态. 通常, 在Promise的参数中, 执行异步操作如ajax请求, 定时器等异步代码.

    Promise有两个实例方法

    • then: 表示注册已决阶段的处理函数, 可以有两个参数
      • 第一个参数是一个函数, 表示注册一个已决阶段resolved状态的处理函数, 该函数接收的参数data表示在之前未决阶段执行代码中, resolve函数传入的数据参数
      • 第二个参数可选, 也是一个函数, 表示注册一个已决阶段rejected状态的处理函数, 该函数接收的参数error表示之前未决阶段执行代码中, reject函数传入的错误信息
    • catch: 表示注册已决阶段rejected状态的处理函数, 效果同then方法的第二个参数
      then和catch方法都返回一个新的Promise

    Promise有几个静态方法

    • resolve: 直接返回一个resolved状态的Promise
    • reject: 直接返回一个rejected状态的Promise
    • race: 返回一个Promise, 参数为多个Promise组成的数组, 只要有一个变为resolved状态, 它就变为resolved状态, 当有一个变为rejected时, 它就rejected
    • all: 返回一个Promise, 参数为多个Promise组成的数组, 只有当全部Promise都变成resolved状态时, 它才变为resolved, 当有一个变为rejected时, 它就rejected

    代码如下

    const MyPromise = (function (undefined) {
        const PENDING = 'pending',
            RESOLVED = 'resolved',
            REJECTED = 'rejected';
            
        const PromiseStatus = Symbol('PromiseStatus');      // 内部变量, 表示promise当前的状态
        const PromiseValue = Symbol('PromiseValue');        // 内部变量, 表示promise的值
        const changeStatus = Symbol('changeStatus');        // 内部函数, 改变promise的状态
        const thenables = Symbol('thenables');              // 所有的resolved状态的处理函数的队列
        const catchables = Symbol('catchables');            // 所有的rejected状态的处理函数的队列
        const settleHandler = Symbol('settleHandler');      // 内部函数, 注册处理函数
        const linkPromise = Symbol('linkPromise');          // 内部函数, 返回新的Promise
        
        return class MyPromise {
            
            constructor(executor) {
                this[PromiseStatus] = PENDING;
                this[PromiseValue] = undefined;
                this[catchables] = [];
                this[thenables] = [];
                
                // 使用箭头函数的形式来定义resolve和reject函数是为了保持this的指向
                const resolve = (data) => {
                    this[changeStatus](RESOLVED, data, this[thenables]);
                }
                
                const reject = (error) => {
                    this[changeStatus](REJECTED, error, this[catchables]);
                }
                
                // 在未决阶段代码执行的过程中, 如果发生错误, promise直接进入rejected状态, 所以要对错误进行捕获
                try {
                    executor(resolve, reject);
                } catch(err) {
                    reject(err);
                }
                
            }
            
            /**
            * 改变状态
            * @param {*} newStatus 改变到的目标状态
            * @param {*} newValue promise的值
            * @param {*} 状态改变后, 需要执行哪个状态的队列
            */
            [changeStatus](newStatus, newValue, queue) {
                // 如果promise不是在pending状态了, 那么resolve和reject都不会有任何效果
                if (this[PromiseStatus] !== PENDING) {
                    return;
                }
                
                this[PromiseStatus] = newStatus;
                this[PromiseValue] = newValue;
                queue.forEach(handler => handler(this[PromiseValue]));
            }
            
            
            
            
            /**
            * 内部函数
            * 由于使用then和catch方法时会返回一个新的promise, 如果原先的promise已经是resolved状态, 则新promise也变成resolved状态
            * 那么新的promise怎么知道旧的promise什么时候resolve呢?
            * 答案是如果旧promise注册的then函数执行了, 那么说明它resolve了, 那么新的promise也可以resolve
            * 所以要对旧promise的thenable和catchable函数再封装一层函数, 假设称呼为F, 实际上把这个F添加到旧的promise的队列中
            * 那么只要旧promise状态变化执行了这个F, 也就执行了F里的触发新promise状态改变的代码
            */
            [linkPromise](thenable, catchable) {
                return new MyPromise((resolve, reject) => {
                    
                    // 将thenable和catchable添加注册
                    // 这里第一个参数就是经过再一层包装的函数
                    this[settleHandler]((data) => {
                        
                        // 如果thenable不是一个函数, 说明它没注册处理程序, 那么就直接将旧promise返回的data直接resolve, 由新promise的处理程序去处理
                        if (typeof thenable !== 'function') {
                            resolve(data);
                            return;
                        }
                        exec(thenable, data, resolve, reject);                  
                    }, RESOLVED, this[thenables]);
                    
                    this[settleHandler]((error) => {
                        
                        if (typeof catchable !== 'function') {
                            reject(error);
                            return;
                        }
                        exec(catchable, error, resolve, reject);                    
                    }, REJECTED, this[catchables]);
                });
                
                // 公共代码抽取函数: 新的promise resolve之前注册的处理函数的处理结果
                function exec(handler, data, resolve, reject) {
                    try {
                        const result = handler(data);
                        // 如果之前的处理函数返回的又是一个promise, 称为P. 则新promise当P resolve的时候resolve, 当P reject的时候reject
                        if (result instanceof MyPromise) {
                            result.then( d => {
                                resolve(d);
                            }, e => {
                                reject(e);
                            });
                        } else {
                            // 否则, 直接resolve 这个result结果
                            resolve(result);
                        }
                    } catch (err) {
                        reject(err);
                    }
                }
            }
            
            
            
            /**
            * 内部函数: 如果状态已经处于已决阶段, 则直接执行处理函数, 否则添加到响应的队列中
            * @param {*} handler 处理函数
            * @param {*} immediateStatus 相应的状态
            * @param {*} queue 需要添加到的队列
            */
            [settleHandler](handler, immediateStatus, queue) {
                // 如果handler不是函数, 那么不做任何事情
                if (typeof handler !== 'function') {
                    return;
                }
                
                // 如果当前的状态不是pending, 而是resolved或者rejected, 那么立即运行处理函数
                if (this[PromiseStatus] === immediateStatus) {
                    // 注册的函数是异步执行的, 所以此处使用setTimeout模拟异步
                    // 区别是promise的异步处理函数是加入微队列中, 而setTimeout的处理函数是加入到宏队列中
                    setTimeout(() => {
                        handler(this[PromiseValue]);
                    }, 0);
                } else {
                    queue.push(handler);
                }
            }
            
            
            then(thenable, catchable) {
                return this[linkPromise](thenable, catchable);
            }
            
            catch(catchable) {
                return this[linkPromise](undefined, catchable);
            }
    
                    finally(callback) {
                return this.then(val => {
                    MyPromise.resolve(callback()).then(() => val);
                }, err => {
                    MyPromise.resolve(callback()).catch(() => { throw err });
                });
            }
            
            /**
            * 静态方法
            * 如果参数是一个promise, 则返回promise本身, 否则返回一个新Promise, 并且直接resolve data数据
            * @param {*} 需要直接resolve的内容
            */
            static resolve(data) {
                if (data instanceof MyPromise) {
                    return data;
                } else {
                    return new MyPromise(resolve => {
                        resolve(data);
                    });
                }           
            }
            
            static reject(error) {
                return new MyPromise((resolve, reject) => {
                    reject(error);
                });
            }
            
            static all(proms) {
                return new MyPromise((resolve, reject) => {
                    const results = proms.map(p => {
                        const obj = {
                            result: undefined,
                            isResolved: false,
                        };
                        
                        p.then(data => {
                            obj.isResolved = true;
                            obj.result = data;
                            // 判断如果结果中没有未resolve的了, 那么才可以resolve
                            const unResolved = results.find(r => !r.isResolved);
                            if (!unResolved) {
                                // resolve的内容为所有promise的数据
                                resolve(results.map(rst => rst.result));
                            }
                        }, err => {
                            // 如果有任何一个promise reject了, 那么新promise也reject
                            reject(err);
                        });
                        
                        return obj;
                    });
                });
            }
            
            static race(proms) {
                return new MyPromise((resolve, reject) => {
                    proms.forEach(p => {
                        p.then(data => {
                            resolve(data);
                        }, err => {
                            reject(err);
                        });
                    });
                });
            }
            
        }
    })(void 0);
    

    相关文章

      网友评论

          本文标题:手写Promise实现

          本文链接:https://www.haomeiwen.com/subject/qlqlghtx.html