小編給大家分享一下JavaScript中setTimeout有什么用,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
成都創(chuàng)新互聯(lián)公司專注于尼勒克企業(yè)網(wǎng)站建設(shè),成都響應(yīng)式網(wǎng)站建設(shè),電子商務(wù)商城網(wǎng)站建設(shè)。尼勒克網(wǎng)站建設(shè)公司,為尼勒克等地區(qū)提供建站服務(wù)。全流程按需設(shè)計(jì),專業(yè)設(shè)計(jì),全程項(xiàng)目跟蹤,成都創(chuàng)新互聯(lián)公司專業(yè)和態(tài)度為您提供的服務(wù)
定時(shí)器的介紹
js中有哪些定時(shí)器?
周期定時(shí)器:setInterval()
介紹
setInterval()是按照指定的周期來(lái)調(diào)用定時(shí)器,方法會(huì)不斷的調(diào)用定時(shí)器,直到使用clearInterval()停止或者窗口關(guān)閉。
語(yǔ)法
setInterval(code,millisec,lang)
code:要執(zhí)行的方法體(必選)
millisec:每隔多少毫秒執(zhí)行一次(單位是毫秒,如果設(shè)置為5000,即每5秒執(zhí)行一次)(必選)
lang:指使用的語(yǔ)言(可選)
實(shí)例
通過(guò)setInterval實(shí)現(xiàn)時(shí)鐘效果
效果圖:
??顧名思義,這個(gè)定時(shí)器只會(huì)執(zhí)行一次,和setInterval()的區(qū)別就在這兒了,正是因?yàn)槿绱耍瑂etInterval()才需要使用clearInterval方法去取消定時(shí)器
??setTimeout(code,millisec,lang)????ps:每個(gè)參數(shù)的含義和setInterval()的均相同
??點(diǎn)擊按鈕3秒后彈出“Hello”
菜鳥(niǎo)教程(runoob.com) 點(diǎn)擊按鈕,在等待 3 秒后彈出 "Hello"。
效果圖:
??使用計(jì)時(shí)器ID來(lái)取消計(jì)時(shí)器回調(diào)的發(fā)生,每個(gè)計(jì)時(shí)器都會(huì)返回一個(gè)id,是為了取消定時(shí)器的方法可以獲取到相應(yīng)的計(jì)數(shù)器。
clearInterval(id)
clearTimeout(id)
//設(shè)置超時(shí)調(diào)用 var timeoutId = setTimeout(function (){ alert("hello World"); },1000); //取消掉用的代碼 clearTimeout(timeoutId);
??我們都知道,js是單線程語(yǔ)言,所有的多線程都是假象,都是單線程模擬出來(lái)的。瀏覽器是多進(jìn)程的,而瀏覽器的內(nèi)核(渲染進(jìn)程)是多線程的。
??渲染進(jìn)程中有一個(gè)js引擎線程,這個(gè)線程是用來(lái)處理javaScript腳本的(例如chrome的V8引擎),而我們一直說(shuō)的javaScript是單線程的就是因?yàn)檫@個(gè)。
??那么問(wèn)題來(lái)了,既然js是單線程的,那setTimeout的異步是怎么實(shí)現(xiàn)的呢?js在解析腳本的時(shí)候,會(huì)將任務(wù)分為兩大類,同步任務(wù)和異步任務(wù),它們?cè)诮馕鰰r(shí)會(huì)進(jìn)入不同的場(chǎng)所執(zhí)行。
同步任務(wù):會(huì)進(jìn)入主線程的執(zhí)行棧,也就是js引擎線程管理的地方,按照順序執(zhí)行
異步任務(wù):進(jìn)入Event Table中,并注冊(cè)函數(shù),當(dāng)回調(diào)函數(shù)的條件滿足時(shí),就會(huì)將回調(diào)函數(shù)放進(jìn)Event Queue中,也就是任務(wù)隊(duì)列中。
??當(dāng)主線程中的任務(wù)執(zhí)行完畢后,也就是執(zhí)行棧為空時(shí),就會(huì)去任務(wù)隊(duì)列中看有沒(méi)有事件,如果有的話,就進(jìn)入主線程執(zhí)行,一直這樣循環(huán)下去,這就是事件循環(huán)機(jī)制了,可以參照下面的圖理解一下:
??也許你對(duì)事件循環(huán)機(jī)制的過(guò)程還是不太明白,那么我再解釋清楚一點(diǎn)。例如下面這個(gè)例子:
console.log('start') setTimeout(function(){ console.log('setTimeout') },5000) console.log('end')
執(zhí)行過(guò)程:
開(kāi)始解析,遇到console.log,是同步任務(wù),進(jìn)入主線程,直接執(zhí)行,打印start;
往下走,遇到setTimeout,是異步任務(wù),進(jìn)入Event Table,并注冊(cè)回調(diào)函數(shù);
再往下走,遇到console.log,直接執(zhí)行,打印end;
5s后,將回調(diào)函數(shù)放進(jìn)Event Queue,此時(shí)執(zhí)行棧剛好為空,主線程會(huì)去任務(wù)隊(duì)列中取出這個(gè)回調(diào)函數(shù),執(zhí)行,打印setTimeout
??ps:
第1,3步都是js引擎線程干的事情,主線程執(zhí)行任務(wù);
第2步是渲染進(jìn)程中的事件觸發(fā)線程(專門(mén)管理任務(wù)隊(duì)列的)管理;
第4步是定時(shí)器線程控制的(也就是setTiemout和setInterval所在的進(jìn)程),定時(shí)器線程專門(mén)用來(lái)控制什么時(shí)候?qū)⒒卣{(diào)函數(shù)放進(jìn)任務(wù)隊(duì)列。
??如果看懂了上面的例子,就知道其實(shí)setTimeout的第二個(gè)參數(shù)其實(shí)并不能準(zhǔn)確的控制多少秒后執(zhí)行里面的函數(shù),而是控制多少秒后將這個(gè)函數(shù)放進(jìn)任務(wù)隊(duì)列中;這樣也就同樣可以解釋,為什么有時(shí)候明明設(shè)置的是2秒之后執(zhí)行,卻要等不止2秒(因?yàn)楹苡锌赡芏〞r(shí)線程將回調(diào)函數(shù)放進(jìn)任務(wù)隊(duì)列后,主線程還在執(zhí)行執(zhí)行棧中的任務(wù),需要執(zhí)行棧中的任務(wù)全部執(zhí)行完后才會(huì)去任務(wù)隊(duì)列中取任務(wù))。
??這樣就會(huì)引發(fā)一個(gè)問(wèn)題,我們知道setInterval是隔一定的時(shí)間執(zhí)行一次,現(xiàn)在理解了原理后,就知道其實(shí)是隔一定的時(shí)間定時(shí)器線程將回調(diào)函數(shù)放進(jìn)任務(wù)隊(duì)列中。如果已經(jīng)將回調(diào)函數(shù)放進(jìn)任務(wù)隊(duì)列,但是主線程正在執(zhí)行一個(gè)非常耗時(shí)的任務(wù),當(dāng)這個(gè)任務(wù)執(zhí)行完畢后,主線程去任務(wù)隊(duì)列中取任務(wù),這個(gè)時(shí)候,就會(huì)出現(xiàn)連續(xù)執(zhí)行的情況,也就是說(shuō)setInterval相當(dāng)于失效了。
??這一部分主要是針對(duì)在事件循環(huán)機(jī)制中setTimeout調(diào)順序進(jìn)行舉例子,如果能夠輕松的將例子看懂,就說(shuō)明你是真的懂了事件循環(huán)機(jī)制的一部分,為什么說(shuō)是一部分呢,因?yàn)檫€有一個(gè)宏任務(wù)和微任務(wù)的知識(shí)點(diǎn)還沒(méi)有涉及到,后面的進(jìn)階篇就會(huì)涉及到啦!
console.log('start') setTimeout(function(){ console.log('setTimeout') },0) console.log('end')
打印結(jié)果:(如果前面看懂了的同學(xué)應(yīng)該就會(huì)明白為什么)
分析:其實(shí)和上面那個(gè)例子時(shí)一樣的,只是這個(gè)0會(huì)給我們一種會(huì)立即執(zhí)行的假象,這個(gè)0是說(shuō)明定時(shí)器線程會(huì)立即將回調(diào)函數(shù)放進(jìn)任務(wù)隊(duì)列而已,主線程還是會(huì)將執(zhí)行棧中的兩個(gè)同步任務(wù)執(zhí)行完成后再去任務(wù)隊(duì)列中取任務(wù),所以執(zhí)行順序和這里的秒數(shù)無(wú)關(guān)。而且即使執(zhí)行棧為空,也不會(huì)0秒就執(zhí)行,因?yàn)镠TML的標(biāo)準(zhǔn)規(guī)定,setTimeout不超過(guò)4ms按照4ms來(lái)計(jì)算。
console.log('start') setTimeout(function(){ console.log('setTimeout') }(),0) console.log('end')
打印結(jié)果:(仔細(xì)對(duì)比與例1的區(qū)別)
分析:細(xì)心的同學(xué)會(huì)發(fā)現(xiàn),我將回調(diào)函數(shù)改成了立即執(zhí)行函數(shù),就改變了執(zhí)行的順序。首先我們需要明確的是setTimeout的第一個(gè)參數(shù)是指函數(shù)的返回值,這里回調(diào)函數(shù)為立即執(zhí)行函數(shù)時(shí),返回值就是undefined了,所以會(huì)直接執(zhí)行立即執(zhí)行函數(shù),也就是立即打印setTimeout,而真正的setTimeout函數(shù)就相當(dāng)于沒(méi)起作用。
setTimeout(() => { console.log('setTimeout') },3000) sleep(10000000)//偽代碼,表示這個(gè)函數(shù)要執(zhí)行很久很久
打印結(jié)果:
這個(gè)結(jié)果不說(shuō)也知道,肯定會(huì)打印出setTimeout的,但是重點(diǎn)卻不在這兒~
重點(diǎn)在于,這個(gè)setTimeout是隔很久很久打印出來(lái)的,遠(yuǎn)遠(yuǎn)超過(guò)了3秒,這個(gè)例子也是很明確的體現(xiàn)了js的事件循環(huán)機(jī)制。
??這一部分相對(duì)于基礎(chǔ)篇,加上了作用域以及其他也是比較難以理解的東西,可能還需要補(bǔ)充一些其他知識(shí)才會(huì)明白,我會(huì)盡量講清楚,也會(huì)把我看的參考文章放在下面。
??受到一篇文章的啟發(fā),我們以循序漸進(jìn)的方式來(lái)闡述
問(wèn)題:以下代碼輸出的是什么?
for(var i = 0;i < 5;i++){ console.log(i) }
答案:沒(méi)錯(cuò),你沒(méi)有看錯(cuò),就是一個(gè)簡(jiǎn)單的循環(huán),就像你想的那樣,連續(xù)輸出0,1,2,3,4
問(wèn)題:以下代碼輸出的是什么?如果把時(shí)間改為1000*i輸出的又是什么?
for(var i = 0;i < 5;i++){ setTimeout(function(){ console.log(i) },1000) }
答案:
??時(shí)間為1000時(shí),1秒后會(huì)連續(xù)輸出5個(gè)5;時(shí)間為1000*i時(shí),會(huì)每隔一秒輸出一個(gè)5,一共5個(gè)5
分析:
??由上面的事件循環(huán)機(jī)制我們知道,setTimeout是異步事件,會(huì)放在事件隊(duì)列中等著主線程來(lái)執(zhí)行,這個(gè)時(shí)候for循環(huán)中的i已經(jīng)變成了5,由于定時(shí)器線程是在1秒后直接將5個(gè)setTimeout事件放進(jìn)事件隊(duì)列中,所以主線程在執(zhí)行的時(shí)候就沒(méi)有間隔了;當(dāng)時(shí)間乘上一個(gè)i時(shí),定時(shí)器會(huì)隔1秒將setTimeout事件放入隊(duì)列,就會(huì)出現(xiàn)每隔一秒輸出一個(gè)5的情況。
問(wèn)題:如果想輸出0,1,2,3,4應(yīng)該怎么改?
分析:
??出現(xiàn)上一題的情況主要是因?yàn)樵趕etTimeout的回調(diào)函數(shù)中并沒(méi)有保存每次循環(huán)i的值,最后執(zhí)行的時(shí)候,得到的i就是最后更新的i了(即為5),所以要解決這個(gè)問(wèn)題,思路是要在回調(diào)函數(shù)中保存每次for循環(huán)中的i值。
解決方案1:使用es6中l(wèi)et代替var
分析:let是es6中新增的內(nèi)容,作用和var一樣,都是用來(lái)定義變量,但是最大的差別就是let會(huì)形成塊級(jí)作用域,在本例中,就是每次循環(huán),都會(huì)產(chǎn)生一個(gè)作用域,在該作用域中的變量是一個(gè)固定值,下次i變化時(shí)不會(huì)對(duì)這個(gè)i產(chǎn)生影響,也就是達(dá)到了我們的目標(biāo)。
for(let i = 0;i < 5;i++){ setTimeout(function(){ console.log(i) },1000*i) }
解決方案2:使用閉包
分析:就是直接在setTimeout函數(shù)的外面套一層立即執(zhí)行函數(shù),并將i值作為參數(shù)傳到匿名函數(shù)中(這里的匿名函數(shù)也可以是命名函數(shù)),然后由于setTimeout中回調(diào)函數(shù)用到了匿名函數(shù)中的i,就會(huì)形成閉包。
for(var i = 0;i < 5;i++){ (function(i){ setTimeout(function(){ console.log(i) }, 1000 * i) }) (i) }
延伸:將代碼變成下面這樣會(huì)輸出什么?(去掉匿名函數(shù)中的i)
分析:這里會(huì)輸出5個(gè)5,也就是閉包沒(méi)有起作用,根本原因是i并沒(méi)有傳進(jìn)去,打印的還是最后的i
for (var i = 0; i < 5; i++) { (function () { setTimeout(function () { console.log(i) }, i * 1000) })(i); }
解決方案3:將回調(diào)函數(shù)改成立即執(zhí)行函數(shù)
分析:這個(gè)解決方案其實(shí)不是太好,如果要求是每隔1秒輸出一個(gè)數(shù)字,這個(gè)方法就不適用了;這個(gè)方法會(huì)立馬輸出0,1,2,3,4,原因結(jié)合基礎(chǔ)篇應(yīng)該就明白了
for (var i = 0; i < 5; i++) { setTimeout((function (i){ console.log(i); })(i), i * 1000) }
??這一部分會(huì)涉及到promise,事件循環(huán)機(jī)制,宏任務(wù)和微任務(wù)的內(nèi)容,算是比較難的部分了,如果覺(jué)得比較難看懂,最好先去補(bǔ)一下基礎(chǔ)知識(shí),我這里就簡(jiǎn)單介紹一下。
我這里就不詳細(xì)講了。
宏任務(wù):可以理解成將代碼塊走一遍的過(guò)程,setTimeout和promise都是宏任務(wù),現(xiàn)在不理解沒(méi)關(guān)系,后面會(huì)通過(guò)例子幫助理解
微任務(wù):是在宏任務(wù)執(zhí)行完成之后執(zhí)行的,也是有相應(yīng)的微任務(wù)隊(duì)列存放微任務(wù),比如promise中的then就是微任務(wù)
問(wèn)題:以下代碼輸出的是什么?
setTimeout(function () { console.log(1) }, 0); new Promise(function executor(resolve) { console.log(2); for (var i = 0; i < 10000; i++) { i == 9999 && resolve(); } console.log(3); }).then(function () { console.log(4); }); console.log(5);
答案:(是不是很懵,為什么會(huì)是這樣,下面看我的分析你就知道了)
分析:
進(jìn)入宏任務(wù)(從第一行到最后一行執(zhí)行一遍的過(guò)程),碰到setTimeout,將setTimeout放進(jìn)事件隊(duì)列中;
碰到promise,執(zhí)行console,打印2;
經(jīng)過(guò)循環(huán)后,執(zhí)行console,打印3;
到了then,由于then是微任務(wù),會(huì)在宏任務(wù)執(zhí)行完成后執(zhí)行,放進(jìn)微任務(wù)隊(duì)列;
遇到console,打印5;
至此,第一次的宏任務(wù)執(zhí)行完成,接下來(lái)執(zhí)行微任務(wù)隊(duì)列中的then,打印4;
現(xiàn)在執(zhí)行棧中的任務(wù)都執(zhí)行完了,現(xiàn)在就要去事件隊(duì)列中取事件,此時(shí)執(zhí)行setTimeout這個(gè)宏任務(wù),打印1;
宏任務(wù)微任務(wù)與同步事件異步事件的關(guān)系:
??這些詞都是用來(lái)描述事件的,只是從不同的角度來(lái)描述,就像是胖子矮子與男生女生之間的聯(lián)系
以上是“JavaScript中setTimeout有什么用”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!