細說 Javascript 拾遺篇(四) : setTimeout 和 setInterval

因爲 Javascript 是異步的,所以咱們能夠經過 setTimeoutsetInterval 函數來指定特定時間執行代碼。git

function foo() {}
var id = setTimeout(foo, 1000); // returns a Number > 0

上例中,當 setTimeout 函數被調用時,它會返回一個標誌延時的 ID 並計劃在大約 1000 毫秒後調用 foo 函數,函數 foo 將只會執行一次。
根據 Javascript 引擎的計時方法,以及 Javascript 單線程的本質,所以其餘代碼執行時可能會阻塞此線程,咱們沒法保證 setTimeout 函數內調用的函數會在指定的時間被執行。
setTimeout 函數的第一個參數將會在全局做用域內執行,所以參數內的 this 將會指向全局對象。github

function Foo() {
    this.value = 42;
    this.method = function() {
        // this refers to the global object
        console.log(this.value); // will log undefined
    };
    setTimeout(this.method, 500);
}
new Foo();

這裏要注意一個常犯的錯誤,setTimeout 函數的第一個參數指的是函數對象自己,不能寫成相似 setTimeout(foo(), 1000),由於 foo() 是函數返回值,而不是 foo 自己。異步

setInterval 函數的堆調用

從上文已知,setTimeout 中的回調函數只會執行一次,而 setInterval 函數,正如函數的名字同樣,它會每隔指定時間執行一次回調函數。
即便回調函數的執行被阻塞,setInterval 函數依然會繼續調用更多的回調函數。當間隔時間設置較小時,將會致使回調函數堆積。ide

function foo(){
    // something that blocks for 1 second
}
setInterval(foo, 1000);

上述代碼中,函數 foo 被調用後將被阻塞一秒鐘。函數

處理可能阻塞的代碼

最簡單且最可控的方式就是在回調函數內部使用 setTimeout 函數。學習

function foo(){
    // something that blocks for 1 second
    setTimeout(foo, 1000);
}
foo();

這樣不只封裝了 setTimeout 的調用,同時也阻止了可能存在的回調函數堆積。foo 函數如今能夠本身控制是否繼續或終止。this

手動清除定時器

清除定時器能夠經過傳遞指定的 IDclearTimeoutclearInterval 函數。線程

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

清除全部的定時器

Javascript 中並無內置的函數方法來清除全部的定時器(timeoutinterval),不過咱們能夠使用一種暴力的方法來清除全部的定時器。翻譯

// clear "all" timeouts
for(var i = 1; i < 1000; i++) {
    clearTimeout(i);
}

可是很明顯,因爲指定最大值的限制,還會有定時器沒有被清除掉。因爲 ID 會隨着定時器被調用的增長而增長,所以更好的方法是記錄下最大的 ID 並一塊兒清除。code

// clear "all" timeouts
var biggestTimeoutId = window.setTimeout(function(){}, 1),
i;
for(i = 1; i <= biggestTimeoutId; i++) {
    clearTimeout(i);
}

eval 的隱式使用

setTimeoutsetInterval 函數的第一個參數也能夠接收字符串,可是儘可能不要使用這個功能,由於這會在內部調用 eval 函數來執行這段字符串。

function foo() {
    // will get called
}

function bar() {
    function foo() {
        // never gets called
    }
    setTimeout('foo()', 1000);
}
bar();

因爲 eval 函數並無在上例中被直接調用,所以傳遞到 setTimeout 函數的字符串將會在全局做用域下被執行,因此不會調用函數 bar 內部的 foo 函數。
建議儘可能不要在使用定時器函數時經過字符串形式來傳遞參數。

function foo(a, b, c) {}

// NEVER use this
setTimeout('foo(1, 2, 3)', 1000)

// Instead use an anonymous function
setTimeout(function() {
    foo(a, b, c);
}, 1000)

總結

不要使用字符串做爲 setTimeoutsetInterval 函數的參數,當須要向回調函數中傳遞參數時,咱們能夠用匿名函數的,在匿名函數內部執行回調函數。
另外,儘可能避免使用 setInterval 函數,從而避免可能致使的回調函數堆積現象。

參考

http://bonsaiden.github.io/JavaScript-Garden/#other.timeouts

後言

終於將整個 Javascript Garden 都學習了一遍,基本上每個章節都翻譯了一遍,同時加上了本身的一些想法和筆記,大概花了半個多月的時間,感受這的的確確是個很適合本身的學習方法,有時候忘記某些概念,我立馬就能在本身的博客中找到相關的知識並及時回顧,因爲出自本身的筆下,因此很快就能回憶起來。但願本身能堅持這個好的習慣,也但願本身的博文能給博友們帶來些許的幫助,你們相互學習,共同進步!

相關文章
相關標籤/搜索