JS魔法堂: Native Promise Only源码剖析

简介:

一, 前言                            

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

   Part 1: The Sync Problem(译文:http://segmentfault.com/blog/kk_470661/1190000000586666)

   Part 2: The Inversion Problem(译文:http://segmentfault.com/blog/kk_470661/1190000000591382)

   Part 3: The Trust Problem(译文:http://segmentfault.com/blog/kk_470661/1190000000593885)

   Part 4: The Extension Problem(译文:http://segmentfault.com/blog/kk_470661/1190000000600268)

   Part 5: The LEGO Problem(译文:http://segmentfault.com/blog/kk_470661/1190000000611040)

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

   NPO@github

 

二, 整体脉络                          

 对于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<this.chain.length; i++) {
                        notifyIsolated(
                                this,
                                (this.state === 1) ? this.chain[i].success : this.chain[i].failure,
                                this.chain[i]
                        );
                }
                this.chain.length = 0;
        }

        // NOTE: This is a separate function to isolate
        // the `try..catch` so that other code can be
        // optimized better
        function notifyIsolated(self,cb,chain) {
                var ret, _then;
                try {
                        if (cb === false) {
                                chain.reject(self.msg);
                        }
                        else {
                                if (cb === true) {
                                        ret = self.msg;
                                }
                                else {
                                        ret = cb.call(void 0,self.msg);
                                }

                                if (ret === chain.promise) {
                                        chain.reject(TypeError("Promise-chain cycle"));
                                }
                                else if (_then = isThenable(ret)) {
                                        _then.call(ret,chain.resolve,chain.reject);
                                }
                                else {
                                        chain.resolve(ret);
                                }
                        }
                }
                catch (err) {
                        chain.reject(err);
                }
        }
复制代码
复制代码
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);
                }
        }
复制代码

 

四, 总结                            

  尊重原创,转载请注明来自: http://www.cnblogs.com/fsjohnhuang/p/4293499.html ^_^肥仔John

 

如果您觉得本文的内容有趣就扫一下吧!捐赠互勉!

分类: JavaScript
0
0
« 上一篇: Java魔法堂:解读基于Type Erasure的泛型
» 下一篇: JS读书心得:《JavaScript框架设计》——第12章 异步处理
posted @ 2015-02-16 06:19 ^_^肥仔John 阅读( 953) 评论( 0) 编辑 收藏
 
相关文章
|
17天前
|
前端开发 JavaScript
用JavaScript 实现一个简单的 Promise 并打印结果
用 JavaScript 实现一个简单的 Promise 并打印结果
|
23天前
|
JavaScript
JS实现简单的打地鼠小游戏源码
这是一款基于JS实现简单的打地鼠小游戏源码。画面中的九宫格中随机出现一个地鼠,玩家移动并点击鼠标控制画面中的锤子打地鼠。打中地鼠会出现卡通爆破效果。同时左上角统计打地鼠获得的分数
48 1
|
17天前
|
JSON 前端开发 JavaScript
在 JavaScript 中,如何使用 Promise 处理异步操作?
通过以上方式,可以使用Promise来有效地处理各种异步操作,使异步代码更加清晰、易读和易于维护,避免了回调地狱的问题,提高了代码的质量和可维护性。
|
14天前
|
前端开发 JavaScript
用HTML CSS JS打造企业级官网 —— 源码直接可用
必看!用HTML+CSS+JS打造企业级官网-源码直接可用,文章代码仅用于学习,禁止用于商业
61 1
|
20天前
|
JavaScript
JS趣味打字金鱼小游戏特效源码
hi fish是一款打字趣味小游戏,捞出海里的鱼,捞的越多越好。这款游戏用于电脑初学者练习打字。初学者可以根据自己的水平设置游戏难度。本段代码可以在各个网页使用,有需要的朋友可以直接下载使用,本段代码兼容目前最新的各类主流浏览器,是一款非常优秀的特效源码!
29 3
|
22天前
|
JavaScript
JS鼠标框选并删除HTML源码
这是一个js鼠标框选效果,可实现鼠标右击出现框选效果的功能。右击鼠标可拖拽框选元素,向下拖拽可实现删除效果,简单实用,欢迎下载
35 4
|
22天前
|
JavaScript
js实现简洁实用的网页计算器功能源码
这是一款使用js实现简洁实用的网页计算器功能源码。可实现比较基本的加减乘除四则运算功能,界面简洁实用,是一款比较基本的js运算功能源码。该源码可兼容目前最新的各类主流浏览器。
24 2
|
21天前
|
移动开发 HTML5
html5+three.js公路开车小游戏源码
html5公路开车小游戏是一款html5基于three.js制作的汽车开车小游戏源代码,在公路上开车网页小游戏源代码。
49 0
html5+three.js公路开车小游戏源码
|
21天前
|
JavaScript
JS趣味打字金鱼小游戏特效源码
hi fish是一款打字趣味小游戏,捞出海里的鱼,捞的越多越好。这款游戏用于电脑初学者练习打字。初学者可以根据自己的水平设置游戏难度。本段代码可以在各个网页使用,有需要的朋友可以直接下载使用,本段代码兼容目前最新的各类主流浏览器,是一款非常优秀的特效源码!
32 0
JS趣味打字金鱼小游戏特效源码
|
23天前
|
JavaScript
js实现的精美彩色tab选项卡切换特效源码
js实现的精美彩色tab选项卡切换特效源码是一段基于JS实现的文件夹tab选项卡切换效果,拥有彩色、单色两种选择,点击标签选项卡可实现相应的变色效果,非常有意思,欢迎对此段代码感兴趣的朋友前来下载使用。
27 2