【轉(zhuǎn)載】一家之言:說(shuō)說(shuō) JavaScript 計(jì)時(shí)器的工作原理

看下面內(nèi)容之前,看一小段代碼,如果讀者能說(shuō)出代碼的用意,那就沒(méi)必要往下看了,因?yàn)槟愣级?/p>

setTimeout(function(){
/* Some long block of code… */
setTimeout(arguments.callee, 10);
}, 10);
setInterval(function(){
/* Some long block of code… */
}, 10);

計(jì)時(shí)器是一個(gè)很牛X的東西,但是很多人其實(shí)只限于知道它的語(yǔ)法,缺乏對(duì)其原理的認(rèn)識(shí)。計(jì)時(shí)器通過(guò)設(shè)定一定的時(shí)間段(毫秒)來(lái)異步的執(zhí)行一段代碼。因?yàn)?Javascript 是一個(gè)單線程語(yǔ)言,計(jì)時(shí)器提供了一種繞過(guò)這種語(yǔ)言限制來(lái)執(zhí)行代碼的能力。
今天就簡(jiǎn)單的來(lái)說(shuō)下計(jì)時(shí)器的工作原理。

JavaScript 提供了三個(gè)函數(shù)來(lái)構(gòu)建和操作計(jì)時(shí)器

  • var id = setTimeout(fn, delay);
  • var id = setInterval(fn, delay);
  • clearInterval(id); clearTimeout(id);

具體的語(yǔ)法我就不多說(shuō)了,可以查手冊(cè)。為了了解計(jì)時(shí)器的工作原理,有一個(gè)概念必須記在心里:時(shí)間延遲不能被保證。什么意思,就是說(shuō)你這樣寫(xiě)setTimeout(fn, 500)并不代表fn肯定在500毫秒之后馬上就執(zhí)行,延遲很可能會(huì)更長(zhǎng)。因?yàn)?JavaScript 是單線程語(yǔ)言,所有的異步事件(包括計(jì)時(shí)器、鼠標(biāo)事件或者一個(gè) XMLHttpRequest 完成)僅僅當(dāng)程序執(zhí)行期間有缺口的時(shí)候才會(huì)執(zhí)行,不是你規(guī)定了什么時(shí)候就什么時(shí)候執(zhí)行,要知道程序員不是萬(wàn)能的,你寫(xiě)的東西最終還是要看瀏覽器臉色的。

下面的這張圖片可以很好的說(shuō)明問(wèn)題,感謝 John Resig 大神。


Timers

從上往下看,左面的數(shù)字代表時(shí)間(毫秒),右面的文字代表了一系列異步事件的設(shè)置和觸發(fā),中間則是代碼塊。最上面的 JavaScript 代碼塊可能是你在瀏覽器載入的時(shí)候執(zhí)行的片段,大概耗時(shí)18毫秒,緊接著下面的 Mouse Click Callback 代碼塊可能是你一個(gè)鼠標(biāo)事件觸發(fā)時(shí)的回調(diào)函數(shù),大概耗時(shí)11毫秒,依次類(lèi)推。

JavaScript 的單線程特性決定了每次只能執(zhí)行一塊,所以當(dāng)?shù)谝粔K代碼執(zhí)行的時(shí)候(它一共運(yùn)行了18毫秒),本身構(gòu)造了兩個(gè)計(jì)時(shí)器,期間可能用戶還點(diǎn)了一下鼠標(biāo)(你有過(guò)在網(wǎng)頁(yè)一打開(kāi)還沒(méi)載完就在那亂點(diǎn)的情況嗎)。按理說(shuō)用戶點(diǎn)完鼠標(biāo)就應(yīng)該馬上執(zhí)行那個(gè)回調(diào)函數(shù),但是不行,JavaScript 執(zhí)行只有一條道嘛,在那18毫秒沒(méi)跑完之前,其他代碼塊想執(zhí)行就只能排隊(duì),沒(méi)空間給你超車(chē)都。那兩個(gè)計(jì)時(shí)器都是10毫秒的延遲,從圖中可以看到,setTimeout也在那18毫秒執(zhí)行結(jié)束之前觸發(fā)了,沒(méi)辦法也排隊(duì)吧。

終于,18毫秒后,天上一道神雷把前面的車(chē)直接劈成空氣了,后面兩個(gè)排隊(duì)的可以過(guò)去了,但是還得一個(gè)一個(gè),不能并列,那誰(shuí)先過(guò)去呢?是不是兩個(gè)人在那劃拳?不是的,瀏覽器說(shuō)的算,瀏覽器說(shuō),鼠標(biāo)單擊事件先過(guò)去,setTimeout只能繼續(xù)等11毫秒。注意看圖,在鼠標(biāo)事件回調(diào)函數(shù)執(zhí)行的時(shí)候,又一個(gè)計(jì)時(shí)器事件觸發(fā)了(setInterval),等著,而且必須排在setTimeout的后面。

11毫秒過(guò)去了,setTimeout 終于可以過(guò)去了,注意看,setInterval 的第二次觸發(fā)了,雖然它第一次都在排隊(duì)呢,如果這個(gè)時(shí)候還向往常一樣排隊(duì),最后是什么情況,setTimeout執(zhí)行完了,就會(huì)連續(xù)執(zhí)行兩個(gè)setInterval,你設(shè)置的延遲沒(méi)用了都。所以瀏覽器還是比較智能的,它在處理setInterval的時(shí)候,如果發(fā)現(xiàn)已經(jīng)有排隊(duì)的,就直接把新來(lái)的 Kill 掉。

接著看,輪到排隊(duì)的 setInterval 第一次觸發(fā)開(kāi)始執(zhí)行了,它執(zhí)行的時(shí)候,第三次觸發(fā)又到了,這一次沒(méi)有排隊(duì)了,所以瀏覽器沒(méi)把它 Kill 掉,給丫排隊(duì)的機(jī)會(huì),所以你會(huì)發(fā)現(xiàn)這兩次的setInterval的執(zhí)行沒(méi)有間隔的,如果你做一個(gè)幻燈片,遇到這種情況就要好好想想自己的代碼是不是有問(wèn)題了。

最后,再也沒(méi)有別的因素干擾了 setInterval 了(假如用戶被 MM 叫走了),setInterval 就按照你想要的步驟執(zhí)行了。
講到這里,開(kāi)頭的代碼可以理解了吧。

setTimeout(function(){
/* Some long block of code… */
setTimeout(arguments.callee, 10);
}, 10);
setInterval(function(){
/* Some long block of code… */
}, 10);

這兩個(gè)函數(shù)看起來(lái)效果一樣,其實(shí)不然,第一個(gè)代碼塊總會(huì)延遲10毫秒執(zhí)行,雖然大多時(shí)候是大于10毫秒的。而第二個(gè)每到10毫秒就嘗試執(zhí)行,不管之前的觸發(fā)執(zhí)行了沒(méi)有。

總結(jié)起來(lái)四條:

  • JavaScript 引擎只有一個(gè)線程,它會(huì)迫使某些異步事件排隊(duì)
  • setTimeout 和 setInterval 在執(zhí)行異步代碼的時(shí)候有很大區(qū)別
  • 假如一個(gè)計(jì)時(shí)器被阻止執(zhí)行,它會(huì)等待知道遇到一個(gè)代碼執(zhí)行空隙,通常時(shí)間比預(yù)計(jì)的要長(zhǎng)
  • Intervals 可能會(huì)一個(gè)挨著一個(gè)執(zhí)行,如果回調(diào)函數(shù)的執(zhí)行時(shí)間大于間隔

翻譯鏈接:http://www.daqianduan.com/1112.html
原文鏈接:https://johnresig.com/blog/how-javascript-timers-work/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 一、JS前言 (1)認(rèn)識(shí)JS 也許你已經(jīng)了解HTML標(biāo)記(也稱為結(jié)構(gòu)),知道了CSS樣式(也稱為表示),會(huì)使用HT...
    凜0_0閱讀 2,945評(píng)論 0 8
  • 第1章 認(rèn)識(shí)JS JavaScript能做什么?1.增強(qiáng)頁(yè)面動(dòng)態(tài)效果(如:下拉菜單、圖片輪播、信息滾動(dòng)等)2.實(shí)現(xiàn)...
    mo默22閱讀 1,526評(píng)論 0 5
  • 從根本上講,了解JavaScript計(jì)時(shí)器的工作方式很重要。通常,他們的行為是非直覺(jué)的,因?yàn)樗麄冊(cè)诘膯尉€程。讓我們...
    __Seve閱讀 1,423評(píng)論 0 1
  • JavaScript提供定時(shí)執(zhí)行代碼的功能,叫做定時(shí)器(timer),主要由setTimeout()和setInt...
    晚晴幽草閱讀 1,729評(píng)論 1 18
  • 今天看到這篇文章,學(xué)到了不少東西 特此發(fā)出來(lái) 和大家分享JavaScript的setTimeout與setInte...
    LiLi原上草閱讀 279評(píng)論 0 0

友情鏈接更多精彩內(nèi)容