JS定時(shí)器機(jī)制詳解

前言

JavaScript 提供定時(shí)執(zhí)行代碼的功能,叫做定時(shí)器(timer),主要由setTimeout()setInterval()這兩個(gè)函數(shù)來(lái)完成。它們向任務(wù)隊(duì)列添加定時(shí)任務(wù)。在了解定時(shí)器前,你首頁(yè)也要對(duì)事件循環(huán)機(jī)制有一定的了解??梢韵乳喿x這篇文章Js事件循環(huán)(Event Loop)機(jī)制。

基本概念

什么是定時(shí)器?

定時(shí)器是一種異步任務(wù),通常瀏覽器都有一個(gè)獨(dú)立的定時(shí)器模塊,定時(shí)器的延遲時(shí)間就由定時(shí)器模塊來(lái)管理,當(dāng)某個(gè)定時(shí)器到了可執(zhí)行狀態(tài),就會(huì)被加入主線程隊(duì)列。

定時(shí)器的運(yùn)行機(jī)制是什么?

定時(shí)器的運(yùn)行機(jī)制,是將指定的代碼移出本輪事件循環(huán),等到下一輪事件循環(huán),再檢查是否到了指定時(shí)間。如果到了,就執(zhí)行對(duì)應(yīng)的代碼;如果不到,就繼續(xù)等待。

定時(shí)器解決了什么問(wèn)題?

由于JS的單線程特性,定時(shí)器提供了一種跳出單線程限制的方法,即讓一段代碼在一定毫秒之后,再異步執(zhí)行。

基本用法

setTimeout()

setTimeout函數(shù)用來(lái)指定某個(gè)函數(shù)或某段代碼,在多少毫秒之后執(zhí)行。它返回一個(gè)整數(shù),表示定時(shí)器的編號(hào),以后可以用來(lái)取消這個(gè)定時(shí)器。

var timer = setTimeout(func|code, delay);

clearTimeout(timer);

setTimeout接受兩個(gè)參數(shù):

  • func|code: 是將要推遲執(zhí)行的函數(shù)名或者一段代碼。
  • delay: 是推遲執(zhí)行的毫秒數(shù)。
  • clearTimeout(): 是取消對(duì)應(yīng)的定時(shí)器的函數(shù)。

除了前兩個(gè)參數(shù),setTimeout還允許更多的參數(shù)。它們將依次傳入推遲執(zhí)行的函數(shù)(回調(diào)函數(shù))。

舉個(gè)栗子:

setTimeout(function (a,b) {
  console.log(a + b);
}, 1000, 1, 1);

setInterval()

setInterval函數(shù)的用法與setTimeout完全一致,區(qū)別僅僅在于setInterval指定某個(gè)任務(wù)每隔一段時(shí)間就執(zhí)行一次,也就是無(wú)限次的定時(shí)執(zhí)行。

var timer = setInterval(function() {
  console.log(2);
}, 1000)

clearInterval(timer)

上面代碼中,每隔1000毫秒就輸出一個(gè)2,會(huì)無(wú)限運(yùn)行下去,直到關(guān)閉當(dāng)前窗口。clearInterval函數(shù)是用來(lái)取消對(duì)應(yīng)的定時(shí)器的。其他的跟setTimeout基本一樣。

深入機(jī)制

定時(shí)器不是JavaScript的一項(xiàng)功能,而是作為對(duì)象和方法的一部分,在瀏覽器中使用。也就是說(shuō),在非瀏覽器環(huán)境中使用JavaScript,很可能定時(shí)器不存在來(lái)。

我們都知道JavaScript是單線程的,這也決定了在異步事件(鼠標(biāo)單擊、定時(shí)器等)程序的處理中,在線程中沒有代碼的時(shí)候才會(huì)執(zhí)行。即處理程序需要排隊(duì)執(zhí)行,且不會(huì)被其他處理程序中斷。

下面通過(guò)例子來(lái)了解定時(shí)器的詳細(xì)機(jī)制:

image

  • 0ms時(shí),處啟動(dòng)一個(gè)10ms延遲的定時(shí)器,以及一個(gè)10ms間隔定時(shí)器。
  • 6ms時(shí),觸發(fā)鼠標(biāo)點(diǎn)擊事件。
  • 10ms時(shí),定時(shí)器和第一個(gè)間隔定時(shí)器都過(guò)期了(由于主程序還在執(zhí)行,所以定時(shí)器仍然在排隊(duì),等待空閑在執(zhí)行)。
  • 由于定時(shí)器和點(diǎn)擊事件都是異步事件,所以他們會(huì)進(jìn)行事件排隊(duì),當(dāng)主程序的同步事件執(zhí)行完成(即在18ms之后),線程空閑時(shí)才執(zhí)行。
  • 18ms時(shí),主線程執(zhí)行完畢,開始執(zhí)行隊(duì)列里面的事件,隊(duì)列里面現(xiàn)在有鼠標(biāo)單擊事件、setTimeout定時(shí)器和setInterval定時(shí)器。
  • 20ms時(shí),由于隊(duì)列里面有setInterval定時(shí)器,所以第二個(gè)到期的間隔定時(shí)器就會(huì)作廢處理。
  • 28ms時(shí),單擊事件執(zhí)行完成,并且在10ms就應(yīng)該執(zhí)行的setTimeout定時(shí)器,現(xiàn)在才開始執(zhí)行。
  • 30ms時(shí),第三個(gè)setInterval定時(shí)器到期,因隊(duì)列中有間隔定時(shí)器,所以第三個(gè)也作廢。
  • 34ms時(shí),setTimeout定時(shí)器執(zhí)行完成,開始執(zhí)行setInterval,但由于第一個(gè)間隔定時(shí)器在42ms時(shí)結(jié)束,所以40ms時(shí),到期的第二個(gè)間隔定時(shí)器,又要進(jìn)行排隊(duì)等待。
  • 47ms時(shí),由于第二個(gè)setInterval可以在第三個(gè)間隔定時(shí)器50ms到期時(shí)執(zhí)行完,所以不需要排隊(duì)直接執(zhí)行。

根據(jù)上面的流程進(jìn)行小結(jié):

  • 事件排隊(duì):同時(shí)發(fā)生了這么多事情,由于js的單線程特性,當(dāng)線程正在執(zhí)行狀態(tài),有異步事件觸發(fā)時(shí),它就會(huì)排隊(duì),并且在線程空閑時(shí)才進(jìn)行執(zhí)行。并且依照先進(jìn)先出的順序執(zhí)行(先排隊(duì)的先執(zhí)行)。
  • setInterval調(diào)用被廢棄:在線程被占用的情況下,并且隊(duì)列中已經(jīng)有setInterval在排隊(duì),則下一個(gè)到期的setInterval會(huì)被廢棄。
  • 定時(shí)器無(wú)法保證準(zhǔn)時(shí)執(zhí)行回調(diào)函數(shù):在主線程還沒有結(jié)束,即使定時(shí)器時(shí)間到期仍然不會(huì)執(zhí)行,必須等到主程序同步代碼全部執(zhí)行完。
  • setTimeout和setInterval的區(qū)別:其最主要的區(qū)別是功能上的區(qū)別,setTimeout只延遲執(zhí)行一次,setInterval按時(shí)間周期性的執(zhí)行。

其他相關(guān)知識(shí):

  • 定時(shí)器不能非常細(xì)?;目刂茍?zhí)行的時(shí)間,建議在15ms以上。
  • 可以使用定時(shí)器來(lái)分解長(zhǎng)時(shí)間運(yùn)行的任務(wù),

結(jié)語(yǔ)

即使設(shè)置了時(shí)間,如果使用不當(dāng)?shù)脑挘〞r(shí)器還是不會(huì)保證準(zhǔn)時(shí)執(zhí)行回調(diào)函數(shù)。而通過(guò)本文,相信你以前對(duì)定時(shí)器出現(xiàn)過(guò)類似問(wèn)題有了一定的了解。希望大家熟練理解原理,大家加油!!

?著作權(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)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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