手写Promise实现

2020-05-02  本文已影响0人  风雅欢乐

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

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

Promise的状态

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

Promise的使用

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

Promise有两个实例方法

Promise有几个静态方法

代码如下

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);
上一篇下一篇

猜你喜欢

热点阅读