JavaScript定時(shí)器實(shí)現(xiàn)的原理分析
JavaScript中的定時(shí)器大家基本在平時(shí)的開發(fā)中都遇見過吧,但是又有多少人去深入的理解其中的原理呢?下面我們就來分析一下定時(shí)器的實(shí)現(xiàn)原理。
一、儲備知識
在我們在項(xiàng)目中一般會遇見過這樣的兩種定時(shí)器,第一種是setTimeOut,第二種是setInterval,這兩種定時(shí)器有如下的區(qū)別:
1、setTimeout允許設(shè)置一個(gè)超時(shí)對象,超時(shí)后執(zhí)行這個(gè)對象,但是只執(zhí)行一次,無周期
2、setInternval允許設(shè)置一個(gè)超時(shí)對象,超時(shí)后執(zhí)行這個(gè)對象,周期等于超時(shí)對象指定的時(shí)間,周期為無限循環(huán)
舉一個(gè)簡單的例子來說明一下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>blog案例</title> </head> <body> <script type="text/javascript"> setTimeout("alert('this is test')",2000); setInterval("console.log('demo');",1000); </script> </body> </html>
這個(gè)運(yùn)行后的結(jié)果是彈出了一次對話框,然后在控制臺可以看到每1秒鐘會向其中輸出demo字樣
二、定時(shí)器原理初識
那么問題來了,如下的代碼運(yùn)行的時(shí)候會出現(xiàn)什么情況呢?
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>blog案例</title> </head> <body> <script type="text/javascript"> setTimeout("alert('定時(shí)器!')",0); alert("測試") </script> </body> </html>
是先執(zhí)行alert("測試"),還是先執(zhí)行alert("定時(shí)器")呢,那么我們就來運(yùn)行一下吧!
運(yùn)行后的結(jié)果是先彈出測試字樣的彈出框,然后才彈出定時(shí)器字樣的彈出框,為什么會這樣呢?不是定時(shí)器的時(shí)間為0就即可執(zhí)行嗎?
答案不是這樣的,因?yàn)镴S眾所周知是單線程的,所以很多人會認(rèn)為在上面的例子中會先阻塞等待定時(shí)器執(zhí)行完成后再執(zhí)行下面的語句,但是這個(gè)也就是單線程的一個(gè)缺陷之一吧,為了解決這個(gè)問題,引入了異步機(jī)制。異步機(jī)制主要是利用一個(gè)我們平時(shí)很少去關(guān)注的一個(gè)知識點(diǎn)——瀏覽器的多線程。究竟什么是瀏覽器的多線程呢?
三、瀏覽器的多線程
這里我們就來講解一下,眾所周知,JS是單線程的,但是對于瀏覽器來說JS的執(zhí)行只不過是在瀏覽器眾多現(xiàn)成中的一條,我們稱之為JS引擎線程。而瀏覽器的其他線程則是通過JS引擎線程在執(zhí)行到某個(gè)特定的功能之后指定給瀏覽器的對應(yīng)線程。具體的原理詳見圖示:
從這張圖我們可以知道JS引擎線程首先執(zhí)行回調(diào)函數(shù)塊,然后是執(zhí)行點(diǎn)擊事件回調(diào),接著是執(zhí)行定時(shí)器的線程,最后在執(zhí)行其他的線程。
以下面的代碼我們來分析一下:
setTimeout("alert('定時(shí)器!')",0); alert("測試")
首先JS線程讀取到setTimeout定時(shí)器,這個(gè)時(shí)候就會執(zhí)行瀏覽器的線程,然后跳過定時(shí)器繼續(xù)執(zhí)行,這個(gè)時(shí)候你就看到了彈出框的內(nèi)容為測試,然后因?yàn)槎〞r(shí)器的時(shí)間為0,所以一執(zhí)行定時(shí)器線程就會即可將彈出框?yàn)槎〞r(shí)器字樣的任務(wù)添加到主線程(JS引擎線程)的隊(duì)列之后,等待JS引擎的調(diào)用,這個(gè)時(shí)候我們看到的結(jié)果是先彈出測試,然后再彈出定時(shí)器
另外我們要注意在HTML5規(guī)范中規(guī)定定時(shí)器的定時(shí)時(shí)間不能小于4ms,如果是小于4ms,則默認(rèn)為4ms,所以在這個(gè)例子中的0,默認(rèn)的是4ms,但是這個(gè)在不通過的瀏覽器中的表現(xiàn)是不同的,但是這個(gè)一般在項(xiàng)目中是沒有什么印象的,這個(gè)只是僅做了解即可。
好的我們將上面的代碼改寫成這樣,然后我們再來看看效果:
<script type="text/javascript"> console.time("test"); setTimeout("for(var i=0;i<1000;i++)console.log('定時(shí)器!');",1000); console.log("測試"); console.timeEnd("test"); </script>
運(yùn)行后的結(jié)果如下:
這里有幾個(gè)知識點(diǎn):
1、console.time和console.timeEnd這兩個(gè)方法是可以獲取在其中間執(zhí)行的語句所用的時(shí)間,從圖中我們可以知道test執(zhí)行的時(shí)間在1ms左右,然而定時(shí)器的定時(shí)時(shí)間是在1000ms左右所以這兩個(gè)語句只能計(jì)算當(dāng)前引擎的執(zhí)行時(shí)間,換句話說就是在瀏覽器中的定時(shí)器模塊的運(yùn)行時(shí)間是這樣是沒法計(jì)算的
2、另外我們可以看到一個(gè)現(xiàn)象就是定時(shí)器在執(zhí)行的時(shí)候不是一千個(gè)定時(shí)器的字樣全都一次性的打印出來,而是幾百幾百的增加,這個(gè)是為什么呢?這里就涉及到了另外的一個(gè)問題,如果是定時(shí)器的時(shí)間到了,但是定時(shí)器中的任務(wù)沒有執(zhí)行完成這個(gè)時(shí)候會怎樣?
我們上面說過就是定時(shí)器的時(shí)間到了的情況下,就會向JS引擎線程添加任務(wù),不論任務(wù)里面的語句是否執(zhí)行完成,都會像JS引擎線程隊(duì)列中添加,但是剩下的未執(zhí)行完成的語句怎么辦呢?
程序執(zhí)行到了定時(shí)器任務(wù)的時(shí)候,就會先把已經(jīng)在定時(shí)器模塊執(zhí)行過的語句加載一次,然后是繼續(xù)執(zhí)行定時(shí)器模塊的剩余語句。(定時(shí)器模塊向JS引擎中添加的任務(wù)相當(dāng)于就是C語言中的一個(gè)指針,指向的是定時(shí)器模塊)
所以,setTimeout我們可以定義為:
在指定時(shí)間內(nèi), 將任務(wù)放入事件隊(duì)列,等待js引擎空閑后被執(zhí)行.
四、setInterval的使用
setInterval最基礎(chǔ)的使用方法是直接當(dāng)一個(gè)循環(huán)定時(shí)器使用,這里就不舉例說明
對于setInterval(fn, 100)容易產(chǎn)生一個(gè)誤區(qū):并不是上一次fn執(zhí)行完了之后再過100ms才開始執(zhí)行下一次fn。 事實(shí)上,setInterval并不管上一次fn的執(zhí)行結(jié)果,而是每隔100ms就將fn放入主線程隊(duì)列,而兩次fn之間具體間隔多久就不一定了,跟setTimeout實(shí)際延遲時(shí)間類似,和JS執(zhí)行情況有關(guān)。具體的延遲效果與內(nèi)存等因素有關(guān)。
五、定時(shí)器的可靠性
雖說定時(shí)器在大部分的情況下都是趨于穩(wěn)定的,但是定時(shí)器在使用的時(shí)候也存在著一些誤差
如下所示:
<script type="text/javascript"> var time1 = new Date().getTime(); setInterval(function(){ var time2 = new Date().getTime(); console.log("setInterval執(zhí)行的差值時(shí)間:"+(time2-time1)); },1000); </script>
運(yùn)行的結(jié)果如下:
從圖中我們基本可以看出定時(shí)器存在著一些小小的誤差就比如第一次的運(yùn)行時(shí)間為1001ms比我們設(shè)定的時(shí)間多出了1ms,所以得出結(jié)論:定時(shí)器不是完全的可靠的,存在極小的誤差。這個(gè)還是在chrome瀏覽器上面測試的結(jié)果,換是在IE瀏覽器測試那又如何呢?
結(jié)果顯示,在IE瀏覽器下面的誤差更大
六、定時(shí)器的妙用
定時(shí)器在項(xiàng)目中除了可以作為定時(shí)的作用外還可以用來做耗時(shí)代碼的優(yōu)化:
我們假設(shè)有這樣的一個(gè)場景,就是在某個(gè)頁面中要渲染50萬個(gè)節(jié)點(diǎn),這個(gè)時(shí)候?qū)τ谝话愕捻?xiàng)目中,直接渲染是不可取的,因?yàn)檫@個(gè)時(shí)候會占用過多的內(nèi)存,導(dǎo)致瀏覽器出現(xiàn)了卡死的狀態(tài),用戶誤以為是頁面卡死而 直接關(guān)閉瀏覽器或者殺死進(jìn)程,即使是用戶不關(guān)閉頁面這樣給用戶的體驗(yàn)也是不好的,這個(gè)時(shí)候我們要怎樣來解決這個(gè)問題呢,我們可以利用定時(shí)器來優(yōu)化這個(gè)問題首先我們可以把50萬個(gè)節(jié)點(diǎn)分成多組,每組渲染 的節(jié)點(diǎn)數(shù)不要過多,然后通過setInterval來進(jìn)行循環(huán)這個(gè)既不阻塞JS引擎線程的運(yùn)行,又不可以提高渲染的消耗時(shí)間。從而達(dá)到最終的優(yōu)化渲染。
七、定時(shí)器使用注意事項(xiàng)
如果是項(xiàng)目中有對個(gè)定時(shí)器的參與那么記得在一個(gè)定時(shí)器執(zhí)行結(jié)束的時(shí)候記得要調(diào)用clearInterval或clearTimeout這兩個(gè)方法來清除定時(shí)器,以免定時(shí)器之間互相干擾出現(xiàn)一些抓摸不定的現(xiàn)象
以上就是本文的全部內(nèi)容,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作能帶來一定的幫助,如果有疑問大家可以留言交流,同時(shí)也希望多多支持腳本之家!
- js定時(shí)器的使用(實(shí)例講解)
- JavaScript定時(shí)器詳解及實(shí)例
- JavaScript暫停和繼續(xù)定時(shí)器的實(shí)現(xiàn)方法
- javascript中SetInterval與setTimeout的定時(shí)器用法
- JavaScript 定時(shí)器 SetTimeout之定時(shí)刷新窗口和關(guān)閉窗口(代碼超簡單)
- 獲取焦點(diǎn)時(shí),利用js定時(shí)器設(shè)定時(shí)間執(zhí)行動(dòng)作
- js定時(shí)器實(shí)現(xiàn)倒計(jì)時(shí)效果
- JavaScript定時(shí)器和優(yōu)化的取消定時(shí)器方法
- Javascript 定時(shí)器調(diào)用傳遞參數(shù)的方法
- js 定時(shí)器setTimeout無法調(diào)用局部變量的解決辦法
- JavaScript定時(shí)器常見用法實(shí)例分析
相關(guān)文章
15個(gè)非常實(shí)用的JavaScript代碼片段
這篇文章主要為大家詳細(xì)介紹了15個(gè)非常實(shí)用的JavaScript代碼片段,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-12-12Bootstrap模態(tài)框水平垂直居中與增加拖拽功能
最近開發(fā)一個(gè)CMS系統(tǒng)使用上了Bootstrap,在開發(fā)一個(gè)添加某些選項(xiàng)時(shí),打算彈出一個(gè)模態(tài)框,但是發(fā)現(xiàn),模態(tài)框不會垂直居中到屏幕上,而是在屏幕上方,通過查閱資料才解決此問題,下面小編給大家分享解決思路2016-11-11原生js實(shí)現(xiàn)焦點(diǎn)輪播圖效果
本文主要分享了原生js實(shí)現(xiàn)焦點(diǎn)輪播圖效果的示例代碼,并解析了實(shí)例中的注意點(diǎn)。具有一定的參考價(jià)值,下面跟著小編一起來看下吧2017-01-01純 JS 實(shí)現(xiàn)放大縮小拖拽功能(完整代碼)
這篇文章主要介紹了純js實(shí)現(xiàn)放大縮小拖拽功能,文中給大家提到了在開發(fā)過程中遇到的一些問題及解決方法,需要的朋友可以參考下2019-11-11