Zone是什么:
官方解釋:zone.js為JavaScript提供了執行上下文,可以在異步任務之間進行持久性傳遞。
最開始我一直沒理解到這句話,學習過程中我也因為自己的一些失誤而一直糾結徘徊,情況是這樣的:
首先我在本地用npm安裝了zone.js,然后我就打開了zone.js的github ———— zoneJs
這一切都很正常,然后看着readme,跟着demo寫下去。下面這樣:

然后問題來了,當我運行的時候,結果根本就不是官方顯示的那樣,我打開調試器,發現全局根本沒有 window.zone ,卻只有window.Zone,
難道是需要new一個?好吧,我new一個。結果還是不對。 好吧,我去看源碼,發現原型根本就沒有beforeTask,afterTask,而后看了很久源碼,
發現run和fork方法的執行邏輯, 根本就不像是官方教程那樣。
最后我終於找到問題了:

是版本問題,我看的教程是0.5版本,看的源碼確是0.6,本地安裝的版本也是默認最新的0.6。
好吧,我承認我英文不好,沒看到DEPRECATED 這個單詞。
安裝0.5,那么這個問題解決了。以后小心...
言歸正傳, 我們在demo中去理解:
zone.fork().run(function () { zone.inTheZone = true; setTimeout(function () { console.log('in the zone 1: ' + !!zone.inTheZone); }, 0); }); console.log('in the zone 2: ' + !!zone.inTheZone); //console //'in the zone 2: false' //'in the zone 1: true'
在demo的基礎上,我加上了數字編號 'in the zone ' + '1' or '2', 當然他們的執行順序,沒什么問題。問題就是為什么第一次輸出是false?
執行run的時候,zone.inTheZone已經變成true了,這個全局對象的屬性應該已經是true了啊!
readme下面還有一句話:Note that the function delayed by setTimeout stays inside the zone
延遲方法已經停留在zone內部,這是什么意思?這時候我又改變了一下代碼:
zone.fork().run(function () { zone.inTheZone = true; console.log(zone); console.log('1: ' + !!zone.inTheZone); setTimeout(function () { console.log(zone); console.log('2: ' + !!zone.inTheZone); }, 0); }); console.log(zone); console.log('3: ' + !!zone.inTheZone);
結果:

注意zone的 id , 全局zone原來一直在變化, 他們的父子關系是如下:父->子,id:1 -> id:2 -> id:3
全局zone最開始默認id為1。
zone.fork.run的時候,zone就創建了一個新zone任務,id為2
執行setTimeout的時候,又創建了一個id為3。
不管運行什么任務,其實全局zone都在變化,所有任務都在全局zone下執行了。
這就是官方說的:zone.js采用猴子補丁(Monkey-patched)的暴力方式將JavaScript中的異步任務都包裹了一層,使得這些異步任務都將運行在zone的上下文中。
好吧,雖然說zone在變化,為什么第一次輸出是false,現在解釋可能就簡單的多了。還得看下源碼:

源碼告訴我們:在執行當前zone任務的時候,全局zone指向當前任務的zone對象,執行完畢后全局zone還原到前一個zone任務。
那么,執行run的時候,全局zone是id2的任務,執行完畢后 id2的zone的inTheZone屬性變成true,全局zone又變成id1的zone對象,
而后馬上輸出了 id1 的 inTheZone 屬性,這個時候 id1 並沒有定義inTheZone,所以是false。
最后執行了 id3 的任務,為什么又輸出是true呢,現在只有 id2 的 inTheZone 才會是true啊!
原因是zone會繼承父zone,如圖:

這樣這個輸出結果為什么是這樣就算搞清楚了。
但是它又是如何給setTimeout添加zone任務的呢,因為run執行時並沒有辦法執行延遲操作,異步操作會被添加到瀏覽器的事件隊列,在下一次事件循環(event loops)中才會被執行。
setTimeout怎么被zone給捕獲的呢。這也是zoneJs比較核心的一個地方:
zone修改了原生setTimeout,我們在控制器里輸入setTimeout,結果如下:
function() { return global.zone[setName].apply(global.zone, arguments); }
當然被修改的還有下面這些延遲事件(截圖自源碼):

所有延遲事件都將被zone捕獲。
zone還提供了執行前后的鈎子函數(hook):
- onZoneCreated:產生一個新的zone對象時的鈎子函數。zone.fork也會產生一個繼承至基類zone的新zone,形成一個獨立的zone上下文;
- beforeTask:zone Task執行前的鈎子函數;
- afterTask:zone Task執行完成后的鈎子函數;
- onError:zone運行Task時候的異常鈎子函數;

demo如下:
var profilingZone = (function () { var time = 0, timer = performance ? performance.now.bind(performance) : Date.now.bind(Date); return { beforeTask: function () { console.log("beforeTask"); this.start = timer(); }, afterTask: function () { console.log("afterTask"); time += timer() - this.start; }, time: function () { return Math.floor(time*100) / 100 + 'ms'; }, reset: function () { time = 0; } }; }()); zone.fork(profilingZone).run(function(){ console.log(profilingZone.time()); setTimeout(function(){console.log(profilingZone.time())}, 200); });
/***console***/
//beforeTask
//0ms
//afterTask
//beforeTask
//1.8ms
//afterTask
這些鈎子函數能幫助我們,任務前后攔截並做一些想要的操作,不管是延遲或不延遲的任務都將被攔截。
zoneJs官網已經不推薦0.5版本了,但是zone0.6幾乎算沒有文檔。
readme上的: See the new API here。
打開這個here,看起來確實也很難理解
zonejs 0.6改動較大,封裝性很強,寫法變化較大,還有待研究
目前ng2已經在升級0.6了,最終修改完畢后,到底會是什么樣子,還不得知,目前我看到的0.6的源碼里,好像已經沒有beforeTask這些鈎子函數了。
總之0.6的代碼太難以理解了。我沒怎么看懂,它可以做什么。
目測ng2要發布正式版,還需要時間。
當然這篇博客還是參考了,破狼的文章 zone.js - 暴力之美
非常感謝他為ng社區做出的貢獻!
