一, 前言
深入學習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, 本文為拜讀文章及源碼后的筆記,以便日后查閱.
二, 整體脈絡
對于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
文章列表