JavaScript Timer?

 Mon, 19 Jan 2009 13:39:04 +0800

幾天前在YUI Blog上看到有Zakas的新書免費試閱,就下載來看了一下。他最後提到了一些必須要了解的timer概念,對於使用setTimeout或是setInterval函數很有幫助。

主要的問題在於,JavaScript的實作主要都是基於single thread模型,而用一個queue的結構來處理非同步的執行(包含DOM上面發生的事件、AJAX的事件、setTimeout的事件、setInterval的事件觸發等等),所以他只保證盡量在指定的時候執行,但不保證時間的精確。jQuery的作者John Resig在他的blog中有一篇文章做了不錯的說明:How JavaScript Timers Work。事實上,所有的程式都是放在一個queue裡面依序執行的,所以只要用一個alert()來暫停執行,其他所有應該要執行的程式也都受影響而停下來。

以下用之前做的在指定時間執行動作的程式來做測試,程式碼如下:

<html>
<body>
<div id="panel"><img src="bg/char1.gif"></div>
<input type="button" value="delay">
<script>
(function(){
    var component = {
        target: null,
        init: function(x) {this.target = x; return this;},
        click: function(f) {this.target.onclick = f; return this;},
        html: function(t) {this.target.innerHTML = t; return this;},
        actionSequencer: function(t) {
            var target = this.target;
            var duration = t.duration|0;
            var action = t.action|[];
            action = t.action.sort(function(a,b){return a[0]-b[0];});
            var drop = t.drop|false;
            var start;
            var curr = start = new Date().getTime();
            var timer = true;
            var currTimeUpdater = window.setInterval(function(){
                if(timer) {
                    curr = new Date().getTime();
                } else {
                    window.clearInterval(currTimeUpdater);
                }
            },1);
            var timerStop = window.setInterval(function(){
                if ((curr - start) > duration) {
                    timer = false;
                    window.clearInterval(timerStop);
                }
            },1);
            var actionHandler = window.setInterval(function(){
                if(timer) {
                    if(action.length>0&&(curr-start) > action[0][0]) {
                        if(!drop) {
                            var a = window.setTimeout(function(){
                                window.clearTimeout(a);
                                action[0][1](target);
                                action.shift();
                                },
                            1);
                        } else {
                            for(var i=0; i<action.length; i++) {
                                if(action[i][0] > curr-start) {
                                    var b = window.setTimeout((function(j){
                                        window.clearTimeout(b);
                                        return function(){
                                            action[j][1](target);
                                        };})(i),1);
                                    return;
                                }
                            }
                        }
                    }
                } else {
                    window.clearInterval(actionHandler);
                }
            },1);
            return this;
        }
    };
    $ = function(x) {
        return component.init(x);
    };
})();
$(document.getElementById("panel")).click(function(){
    $(this).actionSequencer(
    {
        duration: 1100,
        action: [
            [20, function(target){$(target).html('<img src="bg/char1.gif">');}],
            [40, function(target){$(target).html('<img src="bg/char2.gif">');}],
            [60, function(target){$(target).html('<img src="bg/char3.gif">');}],
            [80, function(target){$(target).html('<img src="bg/char5.gif">');}],
            [100, function(target){$(target).html('<img src="bg/char4.gif">');}],
            [120, function(target){$(target).html('<img src="bg/char6.gif">');}],
            [140, function(target){$(target).html('<img src="bg/char7.gif">');}],
            [160, function(target){$(target).html('<img src="bg/char8.gif">');}],
            [180, function(target){$(target).html('<img src="bg/char1.gif">');}]
            [200, function(target){$(target).html('<img src="bg/char1.gif">');}],
            [220, function(target){$(target).html('<img src="bg/char2.gif">');}],
            [240, function(target){$(target).html('<img src="bg/char3.gif">');}],
            [260, function(target){$(target).html('<img src="bg/char5.gif">');}],
            [280, function(target){$(target).html('<img src="bg/char4.gif">');}],
            [300, function(target){$(target).html('<img src="bg/char6.gif">');}],
            [320, function(target){$(target).html('<img src="bg/char7.gif">');}],
            [340, function(target){$(target).html('<img src="bg/char8.gif">');}],
            [360, function(target){$(target).html('<img src="bg/char1.gif">');}]
            [380, function(target){$(target).html('<img src="bg/char1.gif">');}],
            [400, function(target){$(target).html('<img src="bg/char2.gif">');}],
            [420, function(target){$(target).html('<img src="bg/char3.gif">');}],
            [440, function(target){$(target).html('<img src="bg/char5.gif">');}],
            [460, function(target){$(target).html('<img src="bg/char4.gif">');}],
            [480, function(target){$(target).html('<img src="bg/char6.gif">');}],
            [500, function(target){$(target).html('<img src="bg/char7.gif">');}],
            [520, function(target){$(target).html('<img src="bg/char8.gif">');}],
            [540, function(target){$(target).html('<img src="bg/char1.gif">');}]
            [560, function(target){$(target).html('<img src="bg/char1.gif">');}],
            [580, function(target){$(target).html('<img src="bg/char2.gif">');}],
            [600, function(target){$(target).html('<img src="bg/char3.gif">');}],
            [620, function(target){$(target).html('<img src="bg/char5.gif">');}],
            [640, function(target){$(target).html('<img src="bg/char4.gif">');}],
            [660, function(target){$(target).html('<img src="bg/char6.gif">');}],
            [680, function(target){$(target).html('<img src="bg/char7.gif">');}],
            [700, function(target){$(target).html('<img src="bg/char8.gif">');}],
            [720, function(target){$(target).html('<img src="bg/char1.gif">');}]
            [740, function(target){$(target).html('<img src="bg/char1.gif">');}],
            [760, function(target){$(target).html('<img src="bg/char2.gif">');}],
            [780, function(target){$(target).html('<img src="bg/char3.gif">');}],
            [800, function(target){$(target).html('<img src="bg/char5.gif">');}],
            [820, function(target){$(target).html('<img src="bg/char4.gif">');}],
            [840, function(target){$(target).html('<img src="bg/char6.gif">');}],
            [860, function(target){$(target).html('<img src="bg/char7.gif">');}],
            [880, function(target){$(target).html('<img src="bg/char8.gif">');}],
            [900, function(target){$(target).html('<img src="bg/char1.gif">');}]
            [920, function(target){$(target).html('<img src="bg/char1.gif">');}],
            [940, function(target){$(target).html('<img src="bg/char2.gif">');}],
            [960, function(target){$(target).html('<img src="bg/char3.gif">');}],
            [980, function(target){$(target).html('<img src="bg/char5.gif">');}],
            [1000, function(target){$(target).html('<img src="bg/char4.gif">');}],
            [1020, function(target){$(target).html('<img src="bg/char6.gif">');}],
            [1040, function(target){$(target).html('<img src="bg/char7.gif">');}],
            [1060, function(target){$(target).html('<img src="bg/char8.gif">');}],
            [1080, function(target){$(target).html('<img src="bg/char1.gif">');}]
        ],
        drop: false
    });
});
$(document.getElementsByTagName('input')[0]).click(function(){alert('test');});
</script>
</body>
</html>

 

測試連結:http://www.fillano.idv.tw/test406.html。跟之前文章中的例子差別主要是在加長執行時間讓小人多轉幾次,並且調整圖片顯示的framerate。在小人轉的時候按下delay按鈕,alert出現的時候,小人也不轉了......不轉的原因就是因為在alert的時候程式暫停執行,當按下確定按鈕時,duration如果已經超過了設定的時間,也就不繼續轉下去。(在Firefox3.0.5、Google Chrome 1.0、Safari 3.2.1、Webkit Nightly Build r39553底下執行結果都一樣。另外如果反應夠快,在alert跳出來時來得及按下確定,那還是會接著指定的時間及動作轉下去,但是就不是連續的了。)

測試結果很明顯,JavaScript是Single Thread的,不論是ajax、各種dom事件、setTimeout、setInterval都不會new一個Thread的。(至少對於我測試過的幾個implementation都是這樣)