Javascript語言裡面看不到什麼叫做Thread的東西,實際上...也沒有。那setTimeout、setInterval以及大大小小的事件,怎麼看起來好像會是同時執行的呢?
事件佇列
John Resig有一篇文章解釋這個問題:http://ejohn.org/blog/how-javascript-timers-work/,另外一篇文章http://dev.opera.com/articles/view/timing-and-synchronization-in-javascript/講得更詳細。
簡單地說,在瀏覽器中,會在頁面下載時執行內嵌在頁面的Javascript,之後是用一個執行緒來handle所有事件。事件觸發後,不論是由UI(例如滑鼠點擊),定期觸發(例如setTimeout/setInterval)或是內部事件(例如XMLHttpRequest的onreadystatechange),最後都會放進一個佇列,依序執行。這個方式類似分時多工,讓瀏覽器看起來好像可以同時做很多事情。
由於都排在一個佇列裡依序執行,萬一有一段程式執行時間較長,就會影響後面程式的執行。因此使用setTimeout或setInterval就會有一個陷阱:時間不精確。儘管setTimeout或setInterval可以設定的timeout時間單位精確到千分之一秒,但是在我自己的電腦上測試,在做動畫效果時,精確到百分之一點五到百分之二秒左右就是極限了,而且跟各個瀏覽器的速度以及電腦速度會有關係。
另外一個會有決定性影響的,是像alert()/confirm()/prompt()這幾個函數,它們會暫停目前程式的執行,所以佇列中接下來的程式也都暫時不會被執行,直到使用者按下按鈕,停止對話框。
我之前寫過一個用timer做動畫效果的測試,做出來的是不會停格的動畫(frame在時間過了就會drop掉),可以用來測試timer的極限以及用其他事件中的alert來中斷執行的狀況...阿,程式太長,沒辦法在分享中出現...所以請參考我的blog好了http://fillano.blog.ithome.com.tw/post/257/22543
有興趣也可以到http://www.fillano.idv.tw/test406.html跑跑看。我設定的總執行時間只有一秒左右,所以按下"delay"按鈕中斷後動畫大概就不會繼續跑(其實有繼續跑,只是時間超過了所以沒有執行...如果無論如何都要把動畫執行完的話,可以把程式裡面判斷時間是否超過的邏輯拿掉)。
事件小陷阱
附加到網頁元素上的事件處理函數,需要注意到一點,就是他的this會指向元素本身。同樣,使用setTimeout及setInterval也會有類似的陷阱,就是事件處理函數內,this會指向window物件。同樣,XMLHttpRequest物件的onreadystatechange事件處理函數,裡面的this也是指向這個XMLHttpRequest物件實體,所以寫事件處理的時候,在onreadystatechange事件處理函數裡面,只要用this就可以指向這個物件了,例如:
<div id="target"></div>
<script>
var $ = {};
$.ajax = new XMLHttpRequest();
$.ajax.open("GET", "iron017.txt", true);
$.ajax.onreadystatechange = function() {
if (this.readyState == 4) {
if (this.status == 200) {
document.getElementById('target').innerHTML = this.responseText;
}
}
}
$.ajax.send(null);
</script>
這樣在處理多個request時還蠻方便的。