博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JS魔法堂: Native Promise Only源码剖析
阅读量:7138 次
发布时间:2019-06-28

本文共 7380 字,大约阅读时间需要 24 分钟。

一, 前言                            

   深入学习Promise的朋友应该都看过<深入理解Promise五部曲>这一系列的文章, 以解除回调地狱之外的观点来剖析Promise更多的内涵,确实十分精彩.

   (译文:http://segmentfault.com/blog/kk_470661/1190000000586666)

   (译文:http://segmentfault.com/blog/kk_470661/1190000000591382)

   (译文:http://segmentfault.com/blog/kk_470661/1190000000593885)

   (译文:http://segmentfault.com/blog/kk_470661/1190000000600268)

   (译文:http://segmentfault.com/blog/kk_470661/1190000000611040)

   NPO(Native Promise Only)是原文作者polyfill的ES6 Promise, 本文为拜读文章及源码后的笔记,以便日后查阅.

  

 

二, 整体脉络                          

 对于Promise实现而言, 主要的主体类型就两个-----Promise和Thenable. NPO中通过MakeDef构建Promise的内部状态结构体def, 并且通过def.chain存储Promise子节点P2-1,P2-2到P2-n, 从而形成一颗Promise树. 而Thenable的内部状态结构体def_wrapper则由MakeDefWrapper构建而成.

  Promise树的结构并不稳定, 实际上每个Promise节点仅与状态为pending的子节点关联, 一旦子节点状态发生变化则断开关联.(该部分在 notify() 中实现)

  {Promise} then(success, failure) , 将success和failure事件处理函数与新生成的Promise子节点绑定, 但订阅的是Promise父节点的状态变化事件.

  另外NPO中通过构建一个异步执行请求队列(scheduling_queue),来收集异步执行请求然后對请求作同一处理,并通过门(cycle)来防止重复执行异步请求处理操作.

 

三, 源码详解                            

  先看看Promise构造函数, 规定仅能通过new方式来构建Promise实例.

function Promise(executor) {                if (typeof executor != "function") {                        throw TypeError("Not a function");                }                if (this.__NPO__ !== 0) {                        throw TypeError("Not a promise");                }                // instance shadowing the inherited "brand"                // to signal an already "initialized" promise                this.__NPO__ = 1;                // 内部结构体                var def = new MakeDef(this);                this["then"] = function then(success,failure) {                        var o = {                                success: typeof success == "function" ? success : true,                                failure: typeof failure == "function" ? failure : false                        };                        // Note: `then(..)` itself can be borrowed to be used against                        // a different promise constructor for making the chained promise,                        // by substituting a different `this` binding.                        o.promise = new this.constructor(function extractChain(resolve,reject) {                                if (typeof resolve != "function" || typeof reject != "function") {                                        throw TypeError("Not a function");                                }                                o.resolve = resolve;                                o.reject = reject;                        });                        // 构建Promise树                        def.chain.push(o);               // 当前Promise节点状态不为pending时,发起异步执行请求事件处理函数                        if (def.state !== 0) {                                schedule(notify,def);                        }                        return o.promise;                };                this["catch"] = function $catch$(failure) {                        return this.then(void 0,failure);                };                try {                        // 调用工厂方法                        executor.call(                                void 0,                                function publicResolve(msg){                                        resolve.call(def,msg);                                },                                function publicReject(msg) {                                        reject.call(def,msg);                                }                        );                }                catch (err) {                        reject.call(def,err);                }        }

   Promise的状态变化放在resolve和reject函数中

function resolve(msg) {                var _then, def_wrapper, self = this;                // already triggered?                if (self.triggered) { return; }                self.triggered = true;                // unwrap                if (self.def) {                        self = self.def;                }                try {                        if (_then = isThenable(msg)) {            // 构造Thenable的内部状态结构体                                def_wrapper = new MakeDefWrapper(self);                                _then.call(msg,                                        function $resolve$(){ resolve.apply(def_wrapper,arguments); },                                        function $reject$(){ reject.apply(def_wrapper,arguments); }                                );                        }                        else {                                self.msg = msg;                                self.state = 1;                                if (self.chain.length > 0) {                                        schedule(notify,self);                                }                        }                }                catch (err) {                        reject.call(def_wrapper || (new MakeDefWrapper(self)),err);                }        }function reject(msg) {                var self = this;                // already triggered?                if (self.triggered) { return; }                self.triggered = true;                // unwrap                if (self.def) {                        self = self.def;                }                self.msg = msg;                self.state = 2;                if (self.chain.length > 0) {                        schedule(notify,self);                }        }

  下面看一下我觉得最亮眼的地方异步执行请求队列, 主要由以下几个部分组成

    1. notify, 遍历def.chain中的所有Promise子节点, 最后由于所有Promise子节的状态均变为fulfilled或rejected因此清空def.chain.

    2. notifyIsolated, 被notify所调用, 用于单独调用绑定在每个Promise子节点的success或failure事件处理函数, 并修改Promse子节点的状态.

    3. scheduling_queue, 存放异步执行请求(以链表实现, 對队列首尾操作性能比数组高).

    4. schedule, 向异步执行请求队列添加元素, 并发起异步请求处理操作.

      上述的1和2两点将作为异步执行请求被存放在3中.代码中各部分则通过4来對队列和异步执行请求作操作.

function notify() {                for (var i=0; i
scheduling_queue = (function Queue() {                var first // 指向队首元素                  , last  // 指向队尾元素                  , item;                function Item(fn,self) {                        this.fn = fn;                        this.self = self;                        this.next = void 0;                }                return {                        // 元素入队                        add: function add(fn,self) {                                item = new Item(fn,self);                                if (last) {                                        last.next = item;                                }                                else {                                        first = item;                                }                                last = item;                                item = void 0;                        },                        // 清空队列                         drain: function drain() {                                var f = first;                                first = last = cycle = void 0;                                // 从队首元素开始遍历所有队列元素                                while (f) {                                        f.fn.call(f.self);                                        f = f.next;                                }                        }                };        })();        // 安排执行状态变化事件的处理函数        function schedule(fn,self) {                scheduling_queue.add(fn,self);                // 防止重复发起异步执行请求                if (!cycle) {                        cycle = timer(scheduling_queue.drain);                }        }

 

四, 总结                            

  尊重原创,转载请注明来自:  ^_^肥仔John

 

你可能感兴趣的文章
Implement strStr()
查看>>
hough T
查看>>
什么是H5?
查看>>
springboot集成shiro实现身份认证
查看>>
cannot download, /home/azhukov/go is a GOROOT, not a GOPATH
查看>>
设计模式之简单工厂模式
查看>>
使用ArcEngine开发自定义Tool并发布为GP服务
查看>>
Intel超低功耗CPU的一些信息
查看>>
Qt之信号与槽
查看>>
PDM/PLM系统授权模型的研究和应用(转载)
查看>>
Winform下的Datagrid的列风格(4)—DataGridComboBoxTableViewColumn
查看>>
上传图片 以及做成缩略图
查看>>
封装和多态
查看>>
POJ - 3041 Asteroids 【二分图匹配】
查看>>
luogu P4198 楼房重建——线段树
查看>>
使用property为类中的数据添加行为
查看>>
程序设计基础知识
查看>>
复变函数与积分变换
查看>>
12. 断点续传的原理
查看>>
C#基础:多态:基类可以定义并实现虚(virtual)方法,派生类可以重写(override)这些方法...
查看>>