文章出處

一, 前言                            

   深入學習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

 


文章列表




Avast logo

Avast 防毒軟體已檢查此封電子郵件的病毒。
www.avast.com


arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()