1. 程式人生 > 其它 >新增javascript程式碼:_JavaScript第二十四篇 高階定時器(上)

新增javascript程式碼:_JavaScript第二十四篇 高階定時器(上)

技術標籤:新增javascript程式碼:

使用 setTimeout()和 s etInterval()建立的定時器可以用於實現有趣且有用的功能。雖然人們 對 JavaScript 的定時器存在普遍的誤解,認為它們是執行緒,其實 JavaScript 是運行於單執行緒的環境中的, 而定時器僅僅只是計劃程式碼在未來的某個時間執行。執行時機是不能保證的,因為在頁面的生命週期中, 不同時間可能有其他程式碼在控制 JavaScript 程序。在頁面下載完後的程式碼執行、事件處理程式、Ajax 回 調函式都必須使用同樣的執行緒來執行。實際上,瀏覽器負責進行排序,指派某段程式碼在某個時間點執行 的優先順序。
可以把 JavaScript 想象成在時間線上執行的。當頁面載入時,首先執行是任何包含在 script元素 中的程式碼,通常是頁面生命週期後面要用到的一些簡單的函式和變數的宣告,不過有時候也包含一些初 始資料的處理。在這之後,JavaScript 程序將等待更多程式碼執行。當程序空閒的時候,下一個程式碼會被 觸發並立刻執行。例如,當點選某個按鈕時,onclick 事件處理程式會立刻執行,只要 JavaScript 程序 處於空閒狀態。

如圖所示:

3a323f54c1c6ce82c65a897a22b80619.png
除了主 JavaScript 執行程序外,還有一個需要在程序下一次空閒時執行的程式碼佇列。隨著頁面在其 生命週期中的推移,程式碼會按照執行順序新增入佇列。例如,當某個按鈕被按下時,它的事件處理程式 程式碼就會被新增到佇列中,並在下一個可能的時間裡執行。當接收到某個 Ajax 響應時,回撥函式的代 碼會被新增到佇列。在 JavaScript 中沒有任何程式碼是立刻執行的,但一旦程序空閒則儘快執行。
定時器對佇列的工作方式是,當特定時間過去後將程式碼插入。注意,給佇列新增程式碼並不意味著對 它立刻執行,而只能表示它會盡快執行。設定一個 150ms 後執行的定時器不代表到了 150ms 程式碼就立刻 執行,它表示程式碼會在 150ms 後被加入到佇列中。如果在這個時間點上,佇列中沒有其他東西,那麼這 段程式碼就會被執行,表面上看上去好像程式碼就在精確指定的時間點上執行了。其他情況下,程式碼可能明 顯地等待更長時間才執行。

請看以下程式碼:

var btn = document.getElementById("my-btn");
btn.onclick = function(){
     setTimeout(function(){
        document.getElementById("message").style.visibility = "visible";
     }, 250);
     //其他程式碼
};
在這裡給一個按鈕設定了一個事件處理程式。事件處理程式設定了一個 250ms 後呼叫的定時器。 點選該按鈕後,首先將 onclick 事件處理程式加入佇列。該程式執行後才設定定時器,再有 250ms 後,指定的程式碼才被新增到佇列中等待執行。實際上,對 setTimeout()的呼叫表示要晚點執行某些 程式碼。
關於定時器要記住的最重要的事情是,指定的時間間隔表示何時將定時器的程式碼新增到佇列,而不 是何時實際執行程式碼。如果前面例子中的 onclick 事件處理程式執行了 300ms,那麼定時器的程式碼至 少要在定時器設定之後的 300ms 後才會被執行。佇列中所有的程式碼都要等到 JavaScript 程序空閒之後才 能執行,而不管它們是如何新增到佇列中的。

重複的定時器

使用 setInterval()建立的定時器確保了定時器程式碼規則地插入佇列中。這個方式的問題在於, 定時器程式碼可能在程式碼再次被新增到佇列之前還沒有完成執行,結果導致定時器程式碼連續執行好幾次, 而之間沒有任何停頓。幸好,JavaScript 引擎夠聰明,能避免這個問題。當使用 setInterval()時,僅 當沒有該定時器的任何其他程式碼例項時,才將定時器程式碼新增到佇列中。這確保了定時器程式碼加入到佇列中的最小時間間隔為指定間隔。

如圖所示:

903e8a355a77786300e41b5c90580082.png
這個例子中的第 1 個定時器是在 205ms 處新增到佇列中的,但是直到過了 300ms 處才能夠執行。當 執行這個定時器程式碼時,在 405ms 處又給佇列添加了另外一個副本。在下一個間隔,即 605ms 處,第一 個定時器程式碼仍在執行,同時在佇列中已經有了一個定時器程式碼的例項。結果是,在這個時間點上的定 時器程式碼不會被新增到佇列中。結果在 5ms 處新增的定時器程式碼結束之後,405ms 處新增的定時器程式碼 就立刻執行。

為了避免setInterval()的重複定時器的這2個缺點,你可以用如下模式使用鏈式setTimeout() 呼叫。

setTimeout(function(){
 //處理中
 setTimeout(arguments.callee, interval);
}, interval);
這個模式鏈式呼叫了 setTimeout(),每次函式執行的時候都會建立一個新的定時器。第二個 setTimeout()呼叫使用了 arguments.callee 來獲取對當前執行的函式的引用,併為其設定另外一 個定時器。這樣做的好處是,在前一個定時器程式碼執行完之前,不會向佇列插入新的定時器程式碼,確保 不會有任何缺失的間隔。而且,它可以保證在下一次定時器程式碼執行之前,至少要等待指定的間隔,避 免了連續的執行。這個模式主要用於重複定時器,如下例所示。
setTimeout(function(){
     var div = document.getElementById("myDiv");
     left = parseInt(div.style.left) + 5;
     div.style.left = left + "px";
     if (left < 200){ 
        setTimeout(arguments.callee, 50);
     }
}, 50);
這段定時器程式碼每次執行的時候將一個
元素向右移動,當左座標在 200 畫素的時候停止。JavaScript 動畫中使用這個模式很常見。