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