setTimeout和setInterval

(轉自:http://www.mxria.com/helps/js_error/trap_error_1572.htm異步

因爲 JavaScript 是異步的,可使用 setTimeout  setInterval 來計劃執行函數。函數

注意:定時處理不是ECMAScript 的標準,它們在 DOM (文檔對象模型) 被實現。this

function foo(){}var id = setTimeout(foo,1000);// 返回一個大於零的數字

 setTimeout 被調用時,它會返回一個 ID 標識而且計劃在未來大約1000 毫秒後調用 foo 函數。 foo函數只會被執行一次。spa

基於 JavaScript 引擎的計時策略,以及本質上的單線程運行方式,因此其它代碼的運行可能會阻塞此線程。 所以無法確保函數會在 setTimeout 指定的時刻被調用。線程

做爲第一個參數的函數將會在全局做用域中執行,所以函數內的 this 將會指向這個全局對象。code

functionFoo(){this.value =42;this.method =function(){// this 指向全局對象
        console.log(this.value);// 輸出:undefined};
    setTimeout(this.method,500);}newFoo();

注意:setTimeout 的第一個參數是函數對象,一個常犯的錯誤是這樣的 setTimeout(foo(), 1000), 這裏回調函數是 foo 的返回值,而不是foo自己。 大部分狀況下,這是一個潛在的錯誤,由於若是函數返回 undefinedsetTimeout 也不會報錯。htm

setInterval 的堆調用

setTimeout 只會執行回調函數一次,不過 setInterval - 正如名字建議的 - 會每隔 X 毫秒執行函數一次。 可是卻不鼓勵使用這個函數。對象

當回調函數的執行被阻塞時,setInterval 仍然會發布更多的毀掉指令。在很小的定時間隔狀況下,這會致使回調函數被堆積起來。ip

function foo(){// 阻塞執行 1 秒}
setInterval(foo,1000);

上面代碼中,foo 會執行一次隨後被阻塞了一分鐘。作用域

 foo 被阻塞的時候,setInterval 仍然在組織未來對回調函數的調用。 所以,當第一次 foo 函數調用結束時,已經有10次函數調用在等待執行。

處理可能的阻塞調用

最簡單也是最容易控制的方案,是在回調函數內部使用 setTimeout 函數。

function foo(){// 阻塞執行 1 秒
    setTimeout(foo,1000);}
foo();

這樣不只封裝了 setTimeout 回調函數,並且阻止了調用指令的堆積,能夠有更多的控制。 foo 函數如今能夠控制是否繼續執行仍是終止執行。

手工清空定時器

能夠經過將定時時產生的 ID 標識傳遞給 clearTimeout 或者 clearInterval 函數來清除定時, 至於使用哪一個函數取決於調用的時候使用的是 setTimeout 仍是 setInterval

var id = setTimeout(foo,1000);
clearTimeout(id);

清除全部定時器

因爲沒有內置的清除全部定時器的方法,能夠採用一種暴力的方式來達到這一目的。

// 清空"全部"的定時器for(var i =1; i <1000; i++){
    clearTimeout(i);}

可能還有些定時器不會在上面代碼中被清除(譯者注:若是定時器調用時返回的 ID 值大於 1000), 所以咱們能夠事先保存全部的定時器 ID,而後一把清除。

隱藏使用 eval

setTimeout  setInterval 也接受第一個參數爲字符串的狀況。 這個特性絕對不要使用,由於它在內部使用了 eval

注意:因爲定時器函數不是 ECMAScript 的標準,如何解析字符串參數在不一樣的 JavaScript 引擎實現中可能不一樣。 事實上,微軟的 JScript 會使用 Function 構造函數來代替 eval 的使用。

function foo(){// 將會被調用}function bar(){function foo(){// 不會被調用}
    setTimeout('foo()',1000);}
bar();

因爲 eval 在這種狀況下不是被直接調用,所以傳遞到 setTimeout 的字符串會自全局做用域中執行; 所以,上面的回調函數使用的不是定義在 bar 做用域中的局部變量 foo

建議不要在調用定時器函數時,爲了向回調函數傳遞參數而使用字符串的形式。

function foo(a, b, c){}// 不要這樣作
setTimeout('foo(1,2, 3)',1000)// 可使用匿名函數完成相同功能
setTimeout(function(){
    foo(a, b, c);},1000)

注意:雖然也可使用這樣的語法 setTimeout(foo, 1000, a, b, c), 可是不推薦這麼作,由於在使用對象的屬性方法時可能會出錯。 (譯者注:這裏說的是屬性方法內,this 的指向錯誤)

結論

絕對不要使用字符串做爲 setTimeout 或者 setInterval 的第一個參數, 這麼寫的代碼明顯質量不好。當須要向回調函數傳遞參數時,能夠建立一個匿名函數,在函數內執行真實的回調函數。

另外,應該避免使用 setInterval,由於它的定時執行不會被 JavaScript 阻塞

相關文章
相關標籤/搜索