Js中setTimeout()和setInterval() 何時被調(diào)用執(zhí)行的用法
定義
setTimeout()和setInterval()經(jīng)常被用來處理延時和定時任務。setTimeout() 方法用于在指定的毫秒數(shù)后調(diào)用函數(shù)或計算表達式,而setInterval()則可以在每隔指定的毫秒數(shù)循環(huán)調(diào)用函數(shù)或表達式,直到clearInterval把它清除。
從定義上我們可以看到兩個函數(shù)十分類似,只不過前者執(zhí)行一次,而后者可以執(zhí)行多次,兩個函數(shù)的參數(shù)也相同,第一個參數(shù)是要執(zhí)行的code或句柄,第二個是延遲的毫秒數(shù)。
很簡單的定義,使用起來也很簡單,但有時候我們的代碼并不是按照我們的想象精確時間被調(diào)用的,很讓人困惑
簡單示例
看個簡單的例子,簡單頁面在加載完兩秒后,寫下Delayed alert!
setTimeout('document.write("Delayed alert!");', 2000);
看起來很合理,我們再看個setInterVal()方法的例子
var num = 0;
var i = setInterval(function() {
num++;
var date = new Date();
document.write(date.getMinutes() + ':' + date.getSeconds() + ':' + date.getMilliseconds() + '<br>');
if (num > 10)
clearInterval(i);
}, 1000);
頁面每隔1秒記錄一次當前時間(分鐘:秒:毫秒),記錄十次后清除,不再記錄。考慮到代碼執(zhí)行時間可能記錄的不是執(zhí)行時間,但時間間隔應該是一樣的,看看結(jié)果
:38:116
:39:130
:40:144
:41:158
:42:172
:43:186
:44:200
:45:214
:46:228
:47:242
:48:256
為什么
時間間隔幾乎是1000毫秒,但不精確,這是為什么呢?原因在于我們對JavaScript定時器存在一個誤解,JavaScript其實是運行在單線程的環(huán)境中的,這就意味著定時器僅僅是計劃代碼在未來的某個時間執(zhí)行,而具體執(zhí)行時機是不能保證的,因為頁面的生命周期中,不同時間可能有其他代碼在控制JavaScript進程。在頁面下載完成后代碼的運行、事件處理程序、Ajax回調(diào)函數(shù)都是使用同樣的線程,實際上瀏覽器負責進行排序,指派某段程序在某個時間點運行的優(yōu)先級。
我們把效果放大一下看看,添加一個耗時的任務
function test() {
for (var i = 0; i < 500000; i++) {
var div = document.createElement('div');
div.setAttribute('id', 'testDiv');
document.body.appendChild(div);
document.body.removeChild(div);
}
}
setInterval(test, 10);
var num = 0;
var i = setInterval(function() {
num++;
var date = new Date();
document.write(date.getMinutes() + ':' + date.getSeconds() + ':' + date.getMilliseconds() + '<br>');
if (num > 10)
clearInterval(i);
}, 1000);
我們又加入了一個定時任務,看看結(jié)果
:9:222
:12:482
:16:8
:19:143
:22:631
:25:888
:28:712
:32:381
:34:146
:35:565
:37:406
這下效果明顯了,差距甚至都超過了3秒,而且差距很不一致。
我們可以可以把JavaScript想象成在時間線上運行。當頁面載入的時候首先執(zhí)行的是頁面生命周期后面要用的方法和變量聲明和數(shù)據(jù)處理,在這之后JavaScript進程將等待更多代碼執(zhí)行。當進程空閑的時候,下一段代碼會被觸發(fā)
除了主JavaScript進程外,還需要一個在進程下一次空閑時執(zhí)行的代碼隊列。隨著頁面生命周期推移,代碼會按照執(zhí)行順序添加入隊列,例如當按鈕被按下的時候他的事件處理程序會被添加到隊列中,并在下一個可能時間內(nèi)執(zhí)行。在接到某個Ajax響應時,回調(diào)函數(shù)的代碼會被添加到隊列。JavaScript中沒有任何代碼是立即執(zhí)行的,但一旦進程空閑則盡快執(zhí)行。定時器對隊列的工作方式是當特定時間過去后將代碼插入,這并不意味著它會馬上執(zhí)行,只能表示它盡快執(zhí)行。
知道了這些后,我們就能明白,如果想要精確的時間控制,是不能依賴于JavaScript的setTimeout函數(shù)的。
重復的定時器
使用 setInterval() 創(chuàng)建的定時器可以使代碼循環(huán)執(zhí)行,到有指定效果的時候,清除interval就可以,如下例
var my_interval = setInterval(function () {
if (condition) {
//..........
} else {
clearInterval(my_interval);
}
}, 100);
但這個方式的問題在于定時器的代碼可能在代碼再次被添加到隊列之前還沒有執(zhí)行完成,結(jié)果導致循環(huán)內(nèi)的判斷條件不準確,代碼多執(zhí)行幾次,之間沒有停頓。不過JavaScript已經(jīng)解決這個問題,當使用setInterval()時,僅當沒有該定時器的其他代碼實例時才將定時器代碼插入隊列。這樣確保了定時器代碼加入到隊列的最小時間間隔為指定間隔。
這樣的規(guī)則帶來兩個問題
1.1. 某些間隔會被跳過
2.2.多個定時器的代碼執(zhí)行之間的間隔可能比預期要小
為了避免這兩個缺點,我們可以使用setTimeout()來實現(xiàn)重復的定時器
setTimeout(function () {
//code
setTimeout(arguments.callee, interval);
}, interval)
這樣每次函數(shù)執(zhí)行的時候都會創(chuàng)建一個新的定時器,第二個setTimeout()調(diào)用使用了agrument.callee 來獲取當前實行函數(shù)的引用,并設置另外一個新定時器。這樣做可以保證在代碼執(zhí)行完成前不會有新的定時器插入,并且下一次定時器代碼執(zhí)行之前至少要間隔指定時間,避免連續(xù)運行。
setTimeout(function () {
var div = document.getElementById('moveDiv');
var left = parseInt(div.style.left) + 5;
div.style.left = left + 'px';
if (left < 200) {
setTimeout(arguments.callee, 50);
}
}, 50);
這段定時器代碼每次執(zhí)行的時候,把一個div向右移動5px,當坐標大于200的時候停止。
- JavaScript定時器setTimeout()和setInterval()詳解
- 精解window.setTimeout()&window.setInterval()使用方式與參數(shù)傳遞問題!
- setInterval()和setTimeout()的用法和區(qū)別示例介紹
- setTimeout()與setInterval()方法區(qū)別介紹
- JavaScript setInterval()與setTimeout()計時器
- JavaScript中定時器setTimeout()和setInterval()的用法
- JavaScript中setInterval()和setTimeout()的用法及區(qū)別
相關文章
JavaScript字符串對象的concat方法實例(用于連接兩個或多個字符串)
這篇文章主要介紹了JavaScript字符串對象的concat方法實例,這個方法用于連接兩個或多個字符串,平時用+號比較多,所以這個方法可能不太常用,需要的朋友可以參考下2014-10-10