前言
JavaScript
提供定時(shí)執(zhí)行代碼的功能队伟,叫做定時(shí)器(timer
)速勇,主要由setTimeout()
和setInterval()
這兩個(gè)函數(shù)來(lái)完成敦间。它們向任務(wù)隊(duì)列添加定時(shí)任務(wù)蜓谋。在了解定時(shí)器前区匣,你首頁(yè)也要對(duì)事件循環(huán)機(jī)制有一定的了解偷拔。可以先閱讀這篇文章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ì)被加入主線(xiàn)程隊(duì)列栅哀。
定時(shí)器的運(yùn)行機(jī)制是什么顷啼?
定時(shí)器的運(yùn)行機(jī)制,是將指定的代碼移出本輪事件循環(huán)昌屉,等到下一輪事件循環(huán)钙蒙,再檢查是否到了指定時(shí)間。如果到了间驮,就執(zhí)行對(duì)應(yīng)的代碼躬厌;如果不到,就繼續(xù)等待。
定時(shí)器解決了什么問(wèn)題扛施?
由于JS
的單線(xiàn)程特性鸿捧,定時(shí)器提供了一種跳出單線(xiàn)程限制的方法,即讓一段代碼在一定毫秒之后疙渣,再異步執(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
是單線(xiàn)程的,這也決定了在異步事件(鼠標(biāo)單擊障贸、定時(shí)器等)程序的處理中错森,在線(xiàn)程中沒(méi)有代碼的時(shí)候才會(huì)執(zhí)行。即處理程序需要排隊(duì)執(zhí)行篮洁,且不會(huì)被其他處理程序中斷涩维。
下面通過(guò)例子來(lái)了解定時(shí)器的詳細(xì)機(jī)制:
-
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之后)豺瘤,線(xiàn)程空閑時(shí)才執(zhí)行吆倦。
-
18ms
時(shí),主線(xiàn)程執(zhí)行完畢坐求,開(kāi)始執(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)在才開(kāi)始執(zhí)行绰更。 -
30ms
時(shí),第三個(gè)setInterval
定時(shí)器到期锡宋,因隊(duì)列中有間隔定時(shí)器儡湾,所以第三個(gè)也作廢。 -
34ms
時(shí)执俩,setTimeout
定時(shí)器執(zhí)行完成徐钠,開(kāi)始執(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的單線(xiàn)程特性,當(dāng)線(xiàn)程正在執(zhí)行狀態(tài),有異步事件觸發(fā)時(shí)根时,它就會(huì)排隊(duì)瘦赫,并且在線(xiàn)程空閑時(shí)才進(jìn)行執(zhí)行。并且依照先進(jìn)先出的順序執(zhí)行(先排隊(duì)的先執(zhí)行)蛤迎。 -
setInterval調(diào)用被廢棄:
在線(xiàn)程被占用的情況下确虱,并且隊(duì)列中已經(jīng)有setInterval
在排隊(duì),則下一個(gè)到期的setInterval
會(huì)被廢棄替裆。 -
定時(shí)器無(wú)法保證準(zhǔn)時(shí)執(zhí)行回調(diào)函數(shù):
在主線(xiàn)程還沒(méi)有結(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ù)脑?huà)怠缸,定時(shí)器還是不會(huì)保證準(zhǔn)時(shí)執(zhí)行回調(diào)函數(shù)诗轻。而通過(guò)本文,相信你以前對(duì)定時(shí)器出現(xiàn)過(guò)類(lèi)似問(wèn)題有了一定的了解揭北。希望大家熟練理解原理扳炬,大家加油!搔体!