Javascript event loop 執行的最小單位?!
Tue, 09 Aug 2011 23:11:23 +0800熟悉Javascript運作原理的人,應該都知道Javascript的非同步執行,是採用一個單一執行緒的方式,把所有事件放在一個queue裡面依序執行。John Resig的文章:How JavaScript Timers Work,裡面對於這樣的運作方式有相當清楚的解釋。
在John Resig舉的例子中,執行的單位看起來都是一個一個事件處理函數...不過實際運作應該不只是這樣。ECMA-262標準中,對於Javascript的執行,定義了三種Execution Context,分別是global, eval以及function。而像setTimeout/setInterval,第一個參數可以是字串或函數,所以排進event loop中執行的程式就有可能是函數或字串。更進一步來看,我們可以利用在文件head元素動態附加一個script元素來載入Javascript,這個時候程式就會跑在global context。
現在好奇的一點是,如果這三種execution context的code都可以排入event loop,那麼是否event loop裡面的執行單位其實就是execution context呢?來寫個簡單的測試看:
<html> <head></head> <body> <div id="panel"></div> </body> </html> <script> var count = 0; var c = true; var result = []; var id1 = setInterval(function(){ var obj=document.createElement('script'); obj.src='test727.js?'+Math.random()*10000+''+new Date().getTime(); document.getElementsByTagName('head')[0].appendChild(obj); if(obj.onreadystatechange) { obj.onreadystatechange = function() { document.getElementsByTagName('head')[0].removeChild(obj); obj = null; }; } else { obj.onload = function() { document.getElementsByTagName('head')[0].removeChild(obj); obj = null; }; } count++; if(count>150) { clearInterval(id1); clearInterval(id2); clearInterval(id3); var stat = 0; result.forEach(function(v,i) { switch(v) { case 1: case 2: if(stat>0) { if(window.console) console.log('wow, global interrupted.'); } stat = 0; break; case 3: stat = 1; case 4: break; case 5: stat = 0; break; } }); } }, 500); var id2 = setInterval(function(){document.getElementById('panel').innerHTML += '1=>function with setInterval<br>';count++;result.push(1)}, 500); var id3 = setInterval("document.getElementById('panel').innerHTML += '2=>eval with setInterval<br>';count++;result.push(2)", 500); </script>動態載入的Javascript檔:
result.push(3); document.getElementById('panel').innerHTML += '3=>start global context<br>'; if(c) {c=false;alert('block by alert()');} result.push(4); document.getElementById('panel').innerHTML += '4=>global context<br>'; count++; result.push(5); document.getElementById('panel').innerHTML += '5=>end global context<br>';
跳出alert時,理論上所有的Javascript的執行都會被block,然後跑完動態載入的global code,不過看起來在Firefox5底下不是這樣XD(Chrome13, IE9, Sfafri5, Opera11.5的結果差不多)
在Firefox5底下跑時,動態載入的Javascript跳出alert然後block住所有程式,關掉alert後繼續執行時,會看到先執行另外兩個interval,然後才執行動態載入的Javascript其餘的程式...所以JaegerMonkey有可能會把函數或Eval插入到global context的任意一行中執行囉?
在Chrome13跑的結果,3,4,5這三個結果是連續的:
在Firefox5跑的結果,在alert之後,1,2這兩個interval插入到3與4,5之間了,所以global code執行是中斷的:
不過我的測試還不是很嚴謹就是了XD