摘要:創建了一個簡單的計時器,在經過給定的時間后,回調函數將會被執行。接受一個計時器由之前兩種計時器返回并且停止計時器回調函數的執行。然而,我們可以注意到,當定時器再一次觸發在計時器回調函數正在執行的時候,這一次定時器回調函數被丟棄了。
最近都在看一些JavaScript原理層面的文章,恰巧看到了jQuery的作者的一篇關于JavaScript計時器原理的解析,于是誠惶誠恐地決定把原文翻譯成中文,一來是為了和大家分享,二來是為了加深自己對于JavaScript的理解。原文鏈接:http://ejohn.org/blog/how-javascript-timers-work/
原文翻譯:
從基礎層面來講,理解JavaScript計時器的工作原理是很重要的。由于JavaScript是單線程的,所以很多時候計時器并不是表現得和我們的直觀想象一樣。讓我們從下面的三個函數開始,它們能夠讓我們有機會去構造和操作計時器。
var id =setTimeout(fn, delay); 創建了一個簡單的計時器,在經過給定的時間后,回調函數將會被執行。這個函數會返回一個唯一的ID,便于在之后某個時間可以注銷這個計時器。
var id = setInterval(fn, delay); -和setTimeout類似,但是每經過一段時間(給定的延時),所傳遞的函數就會被執行一次,直到這個定時器被注銷。
clearInterval(id); clearTimeout(id); -接受一個計時器ID(由之前兩種計時器返回)并且停止計時器回調函數的執行。
為了理解計時器的內部工作原理,我們首先需要了解一個非常重要的概念:計時器設定的延時是沒有保證的。因為所有在瀏覽器中執行的JavaScript單線程異步事件(比如鼠標點擊事件和計時器)都只有在它有空的時候才執行。這最好通過圖片來說明,就如下面這張圖所示:
這一張圖片里面有很多信息需要慢慢消化,但是徹底地理解這張圖片將會讓你對JavaScript異步執行是如何工作的有一個更好的認識。這張圖片是從一維的角度來闡述的:在垂直方向是以毫秒計的時間,藍色的塊代表了
當前正在執行的JavaScript代碼段。比如第一段JavaScript執行了大概18毫秒,鼠標點擊事件大概執行了11毫秒。
由于JavaScript每次只能執行一段代碼(基于它單線程的特性),所以所有這些代碼段都阻塞了其他異步事件的執行。這就意味著,當一件異步事件(比如鼠標點擊,計時器觸發和一個XMLHttpRequest 請求完成)觸發的時候,這些事件的回調函數將排在執行隊列的最后去等待執行(排隊的方式因瀏覽器不同而不同,這里只是一個簡化)。
一開始,在第一段代碼段內,兩個計時器被初始化:一個10ms的setTimeout 和一個10ms的setInterval。由于計時器在哪兒初始化就在那兒開始計時,所以實際上計時器在第一段代碼執行完成之前就觸發了。然而,計時器的回調函數并不是立即執行了(單線程限制了不能這樣做),相反的是,回調函數排在了執行隊列的最后,等到下一個有空的時間去執行。
此外,在第一個代碼塊內我們看到了一個鼠標點擊事件發生了。與之相關的javascript異步事件(我們不可能預測用戶會在什么時候去采取這樣的動作,因此這個事件被視為異步的)并不會立即執行。和計時器一樣的是,它被放到了隊列的最后去等待執行。
在第一個代碼快執行完成的時候,瀏覽器會立即發出這樣的詢問:誰正在等待執行?這個時候,鼠標點擊處理程序和計時器回調函數都在等待執行。瀏覽器選擇了其中一個(鼠標點擊回調函數)并且立即執行它。為了執行,計時器會等到下一個可能執行的時間。
我們注意到,當鼠標點擊事件對應的處理程序正在執行的時候,第一個定時回調函數也要執行了。同定時計時器一樣,它也在隊列的后面等待執行。然而,我們可以注意到,當定時器再一次觸發(在計時器回調函數正在執行的時候),這一次定時器回調函數被丟棄了。如果在執行一大塊代碼塊的時候,你把所有的定時回調函數都放在隊列的最后,結果就是一大串定時回調函數將會沒有間隔的一起執行,直到完成。相反,在把更多定時回調函數放到隊列之前,瀏覽器會靜靜的等待,知道隊列中的所有定時回調函數都執行完成。
事實上,我們可以看到,當interval回調函數正在執行的時候,interval第三次被觸發。這給我們一個很重要的信息:interval并不關心當前誰在執行,它的回調函數會不加區分地進入隊列,即使存在這個回調函數會被丟棄的可能。
最后,當第二個定時回調函數完成執行的時候,我們可以看到javascript引擎已經沒有什么需要執行了。這意味著,瀏覽器現在正在等待一個新的異步事件的發生。我們可以看到在50ms的時候,定時回調函數再一次被觸發。然而,這一次,沒有其他代碼阻塞他的執行了,所以他立即執行了定時回調函數。
讓我們看一個例子來更好地闡述setTimeout 和setInterval的區別。
1 setTimeout(function(){ 2 /* Some long block of code... */ 3 setTimeout(arguments.callee, 10); 4 }, 10); 5 6 setInterval(function(){ 7 /* Some long block of code... */ 8 }, 10);
第一眼看上去這兩段代碼在功能上是等價的,但事實上卻不是。值得注意的是,setTimeout 這段代碼會在每次回調函數執行之后至少需要延時10ms再去執行一次(可能是更多,但是不會少)。但是setInterval會每隔10ms就去嘗試執行一次回調函數,不管上一個回調函數是不是還在執行。
從這里我們能夠學到很多,讓我們來概括一下:
javascript引擎只有一個線程,迫使異步事件只能加入隊列去等待執行。
在執行異步代碼的時候,setTimeout 和setInterval 是有著本質區別的。
如果計時器被正在執行的代碼阻塞了,它將會進入隊列的尾部去等待執行直到下一次可能執行的時間出現(可能超過設定的延時時間)。
如果interval回調函數執行需要花很長時間的話(比指定的延時長),interval有可能沒有延遲背靠背地執行。
上述這一切對于理解js引擎是如果工作的無疑是很重要的知識,尤其是大量的典型的異步事件發生時,對于構建一個高效的應用代碼片段來說是一個非常有利的基礎。
個人見解:
翻譯完成之后,感覺對于javascript異步有了新的認識,但是可能初學者看不太懂這篇文章,于是寫了一個demo,運行在nodejs環境下(瀏覽器不容易模擬)
1 var startTime = new Date(); 2 3 //初始化計時器 4 var start = setTimeout(function() { 5 var end = new Date(); 6 console.log("10ms的計時器執行完成,距離程序開始" + (end - start) + "ms"); 7 }, 10); 8 9 //模擬鼠標點擊事件 10 function asyncReal(data, callback) { 11 process.nextTick(function() { 12 callback(); 13 }); 14 } 15 var asyncStart = new Date(); 16 asyncReal("yuanzm", function() { 17 var asyncEnd = new Date(); 18 console.log("模擬鼠標執行事件完成,花費時間" + (asyncEnd - asyncStart) + "ms"); 19 }) 20 21 //設定定時器 22 count = 1; 23 var interval = setInterval(function() { 24 ++count; 25 if(count === 5) { 26 clearInterval(interval); 27 } 28 console.log("定時器事件"); 29 },10); 30 31 //模擬第一階段代碼執行 32 var first = []; 33 var start = new Date(); 34 for(var i = 0;i < 10000000;i++){ 35 first.push(i); 36 } 37 var end = new Date(); 38 console.log("第一階段代碼執行完成,用時" + (end - start) + "ms");
運行結果如下:
我們按照文中的原理來解釋一下:
一開始設定的計時器并不是在10ms后立即執行,而是被添加到了隊列后面,等到第一階段代碼執行完成才執行,距離開始的時間也不是設定的10ms
鼠標點擊事件同樣因為是異步事件,添加到了隊列后面,等到第一階段代碼執行完成的時候才執行。
鼠標點擊事件先于計時器事件添加到隊列后面
最后定時器才能執行
鄭重聲明
本文章屬于個人原創,如需轉載,請加上原文鏈接:
http://segmentfault.com/a/1190000002633108
另外同樣可以在博客園上面查看本文章:http://www.cnblogs.com/yuanzm/p/4126762.html
也歡迎Follow我的Github:https://github.com/yuanzm
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/85584.html
摘要:說到中的定時器,我們肯定會想到和這兩個函數。第一個回調一執行,又開啟了第二個,這個定時器也是期望延時之后能夠執行它的回調函數??梢杂孟旅娴膱D來概括總結上面對定時器執行原理進行了簡要的分析,希望能夠幫助我們更深入的理解。 說到 javascript 中的定時器,我們肯定會想到 setTimeout() 和 setInterval() 這兩個函數。本文將從 事件循環(Event Loop)...
摘要:一什么是定時器提供了一些原生方法來實現延時去執行某一段代碼,下面來簡單介紹一下設置一個定時器,在定時器到期后執行一次函數或代碼段定時器延遲后執行的函數延遲后執行的代碼字符串,不推薦使用原理類似延遲的時間單位毫秒,默認值為向延遲函數傳遞而外的 一、什么是定時器 JS提供了一些原生方法來實現延時去執行某一段代碼,下面來簡單介紹一下 setTimeout: 設置一個定時器,在定時器到期后執行...
摘要:這兩個函數接受定時器的例如我們上面提到的兩個函數產生的定時器,并停止對定時器中指定函數的調用。注意,定時器雖然觸發了,但是并不會立即執行,它只是把需要延遲執行的函數加入了執行隊列,在線程的某一個可用的時間點,這個函數就能夠得到執行。 擼了今年阿里、頭條和美團的面試,我有一個重要發現....... javascript定時器工作原理是一個重要的基礎知識點。因為定時器在單線程中工作,它們表...
摘要:動畫原文通常,框架會為你處理動畫。整個的動畫過程被分成了很小的步驟,每一個步驟被定時器調用。因為定時器的周期非常短,所以動畫看起來是連續的。已經過去的動畫時間作為分子,計算每一幀通過公式。線性的使動畫以固定的速度進行。 動畫 原文:http://javascript.info/tutori... 通常,框架會為你處理動畫。但是,你可能想知道僅僅用javascript怎么來實現動畫,和可...
摘要:渲染引擎渲染引擎也稱為瀏覽器內核,主要時在瀏覽器窗口中顯示所請求的內容,這是每個瀏覽器的核心部分。 瀏覽器的結構 瀏覽器的主要組件包括: 用戶界面——包括地址欄、前進/后退按鈕、書簽菜單等。除了瀏覽器主窗口顯示用戶請求的頁面外,其他顯示的各個部分都屬于用戶界面。 用戶界面后端——用于繪制基本的窗口小部件,比如組合框和窗口。其公開了與web應用無關的通用接口,而在底層使用操作系統的用戶...
閱讀 1122·2021-09-22 15:32
閱讀 1722·2019-08-30 15:53
閱讀 3253·2019-08-30 15:53
閱讀 1404·2019-08-30 15:43
閱讀 453·2019-08-28 18:28
閱讀 2567·2019-08-26 18:18
閱讀 669·2019-08-26 13:58
閱讀 2528·2019-08-26 12:10