美文网首页
手写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);

相关文章

  • 纯手写实现自己的nodejs promise 库

    纯手写实现自己的nodejs promise 库什么是Promise?promise 链Async/Await后续...

  • 手写Promise实现

    初学ES 6的Promise之后, 感觉这个新的异步处理模型有点绕, 为了加深理解, 所以尝试手写Promise的...

  • 手写promise

    本章节纯粹是对promise手写实现。如果不了解promise自行了解再来学习本章知识。promise初体验首先,...

  • 手写promise

    promise 手写 代码来源:darrell-wheels promise 30分钟,带你实现一个符合规范的 P...

  • 手写Promise

    手写 Promise 我们会通过手写一个符合 Promise/A+ 规范的 Promise 来深入理解它,并且手写...

  • JS 手写Promise

    二、手写Promise 1.Promise的设计和构造方法 2.then方法的设计 3.then方法的优化一 实现...

  • 手写实现 Promise

  • 手写Promise的实现

    1. 简洁版 (没有注释) 2. 详细注释版 3. 测试 4. 简单的伪代码 Promise实现伪代码 定义三种状...

  • 手写实现promise

  • 【手写】promise

    大纲前置知识手写promise resolve reject then 不传参的处理 保证执行顺序,异步实现 th...

网友评论

      本文标题:手写Promise实现

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