本篇文章為大家展示了JavaScript中setTimeOut的使用方法,代碼簡(jiǎn)明扼要并且容易理解,絕對(duì)能使你眼前一亮,通過這篇文章的詳細(xì)介紹希望你能有所收獲。
創(chuàng)新互聯(lián)建站IDC提供業(yè)務(wù):成都服務(wù)器托管,成都服務(wù)器租用,成都服務(wù)器托管,重慶服務(wù)器租用等四川省內(nèi)主機(jī)托管與主機(jī)租用業(yè)務(wù);數(shù)據(jù)中心含:雙線機(jī)房,BGP機(jī)房,電信機(jī)房,移動(dòng)機(jī)房,聯(lián)通機(jī)房。
setTimeout() 方法用于在指定的毫秒數(shù)后調(diào)用函數(shù)或計(jì)算表達(dá)式。
code (必需):(本意是代碼的意思)要調(diào)用的函數(shù)后要執(zhí)行的 JavaScript 代碼串。
millisec(必需):在執(zhí)行代碼前需等待的毫秒數(shù)。
注意:
setTimeout() 只執(zhí)行 code 一次。如果要多次調(diào)用,請(qǐng)使用 setInterval() 或者讓 code 自身再次調(diào)用 setTimeout()。
看到這樣一個(gè)說明,我們明白了它就是一個(gè)定時(shí)器,我們?cè)O(shè)定的函數(shù)就是一個(gè)"鬧鐘",時(shí)間到了它就會(huì)去執(zhí)行.然而聰明的你不禁有這樣一個(gè)疑問,如果是settimeout(fn,0)呢?按照定義的說明,它是否會(huì)立馬執(zhí)行?實(shí)踐是檢驗(yàn)真理的唯一標(biāo)準(zhǔn),讓我們來看看下面的實(shí)驗(yàn)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> </head> <body> <script> alert(1); setTimeout("alert(2)", 0); alert(3); </script> </body> </html>
這是一個(gè)很簡(jiǎn)單的實(shí)驗(yàn),如果settimeout(0)會(huì)立即執(zhí)行,那么這里的執(zhí)行結(jié)果就應(yīng)該是1->2>3 . 然而實(shí)際的結(jié)果卻是1->3->2. 這說明了settimeout(0)并不是立即執(zhí)行.同時(shí)讓我們對(duì)settimeout的行為感到很詭異.
js引擎是單線程執(zhí)行的
我們先把上面的問題放一放.從js語言的設(shè)計(jì)上來看看是否能找到蛛絲馬跡.
我們發(fā)現(xiàn)js語言設(shè)計(jì)的一個(gè)很重要的點(diǎn)是,js是沒有多線程的.js引擎的執(zhí)行是單線程執(zhí)行.這個(gè)特性曾經(jīng)困擾我很久,我想不明白既然js是單線程的,那么是誰來為定時(shí)器計(jì)時(shí)的?是誰來發(fā)送ajax請(qǐng)求的?我陷入了一個(gè)盲區(qū).即將js等同于瀏覽器.我們習(xí)慣了在瀏覽器里面執(zhí)行代碼,卻忽略了瀏覽器本身.js引擎是單線程的,可是瀏覽器卻可以是多線程的,js引擎只是瀏覽器的一個(gè)線程而已.定時(shí)器計(jì)時(shí),網(wǎng)絡(luò)請(qǐng)求,瀏覽器渲染等等.都是由不同的線程去完成的. 口說無憑,咱們依然看一個(gè)例子
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> </head> <body> </body> <script> var isEnd = true; window.setTimeout(function () { isEnd = false;//1s后,改變isEnd的值 }, 1000); while (isEnd); alert('end'); </script> </html>
isEnd默認(rèn)是true的,在while中是死循環(huán)的.最后的alert是不會(huì)執(zhí)行的. 我添加了一個(gè)定時(shí)器,1秒后將isEnd改為false. 如果說js引擎是多線程的,那么在1秒后,alert就會(huì)被執(zhí)行.然而實(shí)際情況是,頁面會(huì)永遠(yuǎn)死循環(huán)下去.alert并沒有執(zhí)行.這很好的證明了,settimeout并不能作為多線程使用.js引擎執(zhí)行是單線程的.
event loop
從上面的實(shí)驗(yàn)中,我們更加疑惑了,settimeout到底做了什么事情呢?
原來還是得從js語言的設(shè)計(jì)上尋找答案.
js引擎單線程執(zhí)行的,它是基于事件驅(qū)動(dòng)的語言.它的執(zhí)行順序是遵循一個(gè)叫做事件隊(duì)列的機(jī)制.從圖中我們可以看出,瀏覽器有各種各樣的線程,比如事件觸發(fā)器,網(wǎng)絡(luò)請(qǐng)求,定時(shí)器等等.線程的聯(lián)系都是基于事件的.js引擎處理到與其他線程相關(guān)的代碼,就會(huì)分發(fā)給其他線程,他們處理完之后,需要js引擎計(jì)算時(shí)就是在事件隊(duì)列里面添加一個(gè)任務(wù). 這個(gè)過程中,js并不會(huì)阻塞代碼等待其他線程執(zhí)行完畢,而且其他線程執(zhí)行完畢后添加事件任務(wù)告訴js引擎執(zhí)行相關(guān)操作.這就是js的異步編程模型.
如此我們?cè)倩剡^頭來看settimeout(0)就會(huì)恍然大悟.js代碼執(zhí)行到這里時(shí),會(huì)開啟一個(gè)定時(shí)器線程,然后繼續(xù)執(zhí)行下面的代碼.該線程會(huì)在指定時(shí)間后往事件隊(duì)列里面插入一個(gè)任務(wù).由此可知settimeout(0)里面的操作會(huì)放在所有主線程任務(wù)之后. 這也就解釋了為什么第一個(gè)實(shí)驗(yàn)結(jié)果是1->3-2 .
由此可見官方對(duì)于settimeout的定義是有迷惑性的.應(yīng)該給一個(gè)新的定義:
在指定時(shí)間內(nèi), 將任務(wù)放入事件隊(duì)列,等待js引擎空閑后被執(zhí)行.
js引擎與GUI引擎是互斥的
談到這里,就不得不說瀏覽器的另外一個(gè)引擎---GUI渲染引擎. 在js中渲染操作也是異步的.比如dom操作的代碼會(huì)在事件隊(duì)列中生成一個(gè)任務(wù),js執(zhí)行到這個(gè)任務(wù)時(shí)就會(huì)去調(diào)用GUI引擎渲染.
js語言設(shè)定js引擎與GUI引擎是互斥的,也就是說GUI引擎在渲染時(shí)會(huì)阻塞js引擎計(jì)算.原因很簡(jiǎn)單,如果在GUI渲染的時(shí)候,js改變了dom,那么就會(huì)造成渲染不同步. 我們需要深刻理解js引擎與GUI引擎的關(guān)系,因?yàn)檫@與我們平時(shí)開發(fā)息息相關(guān),我們時(shí)長(zhǎng)會(huì)遇到一些很奇葩的渲染問題.看這個(gè)例子
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title></title> </head> <body> <table border=1> <tr><td><button id='do'>Do long calc - bad status!</button></td> <td><p id='status'>Not Calculating yet.</p></td> </tr> <tr><td><button id='do_ok'>Do long calc - good status!</button></td> <td><p id='status_ok'>Not Calculating yet.</p></td> </tr> </table> <script> function long_running(status_p) { var result = 0; for (var i = 0; i < 1000; i++) { for (var j = 0; j < 700; j++) { for (var k = 0; k < 300; k++) { result = result + i + j + k; } } } document.querySelector(status_p).innerHTML = 'calclation done' ; } document.querySelector('#do').onclick = function () { document.querySelector('#status').innerHTML = 'calculating....'; long_running('#status'); }; document.querySelector('#do_ok').onclick = function () { document.querySelector('#status_ok').innerHTML = 'calculating....'; window.setTimeout(function (){ long_running('#status_ok') }, 0); }; </script> </body> </html>
我們希望能看到計(jì)算的每一個(gè)過程,我們?cè)诔绦蜷_始,計(jì)算,結(jié)束時(shí),都執(zhí)行了一個(gè)dom操作,插入了代表當(dāng)前狀態(tài)的字符串,Not Calculating yet.和calculating....和calclation done.計(jì)算中是一個(gè)耗時(shí)的3重for循環(huán). 在沒有使用settimeout的時(shí)候,執(zhí)行結(jié)果是由Not Calculating yet 直接跳到了calclation done.這顯然不是我們希望的.而造成這樣結(jié)果的原因正是js的事件循環(huán)單線程機(jī)制.dom操作是異步的,for循環(huán)計(jì)算是同步的.異步操作都會(huì)被延遲到同步計(jì)算之后執(zhí)行.也就是代碼的執(zhí)行順序變了.calculating....和calclation done的dom操作都被放到事件隊(duì)列后面而且緊跟在一起,造成了丟幀.無法實(shí)時(shí)的反應(yīng).這個(gè)例子也告訴了我們,在需要實(shí)時(shí)反饋的操作,如渲染等,和其他相關(guān)同步的代碼,要么一起同步,要么一起異步才能保證代碼的執(zhí)行順序.在js中,就只能讓同步代碼也異步.即給for計(jì)算加上settimeo0t.
settimeout(0)的作用
不同瀏覽器的實(shí)現(xiàn)情況不同,HTML5定義的最小時(shí)間間隔是4毫秒. 使用settimeout(0)會(huì)使用瀏覽器支持的最小時(shí)間間隔.所以當(dāng)我們需要把一些操作放到下一幀處理的時(shí)候,我們通常使用settimeout(0)來hack.
requestAnimationFrame
這個(gè)函數(shù)與settimeout很相似,但它是專門為動(dòng)畫而生的.settimeout經(jīng)常被用來做動(dòng)畫.我們知道動(dòng)畫達(dá)到60幀,用戶就無法感知畫面間隔.每一幀大約16毫秒.而requestAnimationFrame的幀率剛好是這個(gè)頻率.除此之外相比于settimeout,還有以下的一些優(yōu)點(diǎn):
requestAnimationFrame 會(huì)把每一幀中的所有DOM操作集中起來,在一次重繪或回流中就完成,并且重繪或回流的時(shí)間間隔緊緊跟隨瀏覽器的刷新頻率,一般來說,這個(gè)頻率為每秒60幀,每幀大約16毫秒.
在隱藏或不可見的元素中,requestAnimationFrame將不會(huì)進(jìn)行重繪或回流,這當(dāng)然就意味著更少的的cpu,gpu和內(nèi)存使用量。
但它優(yōu)于setTimeout/setInterval的地方在于它是由瀏覽器專門為動(dòng)畫提供的API,在運(yùn)行時(shí)瀏覽器會(huì)自動(dòng)優(yōu)化方法的調(diào)用,并且如果頁面不是激活狀態(tài)下的話,動(dòng)畫會(huì)自動(dòng)暫停,有效節(jié)省了CPU開銷。
總結(jié):
瀏覽器的內(nèi)核是多線程的,它們?cè)趦?nèi)核制控下相互配合以保持同步,一個(gè)瀏覽器至少實(shí)現(xiàn)三個(gè)常駐線程:javascript引擎線程,GUI渲染線程,瀏覽器事件觸發(fā)線程。
javascript引擎是基于事件驅(qū)動(dòng)單線程執(zhí)行的.JS引擎一直等待著任務(wù)隊(duì)列中任務(wù)的到來,然后加以處理,瀏覽器無論什么時(shí)候都只有一個(gè)JS線程在運(yùn)行JS程序。
當(dāng)界面需要重繪(Repaint)或由于某種操作引發(fā)回流(reflow)時(shí),該線程就會(huì)執(zhí)行。但需要注意 GUI渲染線程與JS引擎是互斥的,當(dāng)JS引擎執(zhí)行時(shí)GUI線程會(huì)被掛起,GUI更新會(huì)被保存在一個(gè)隊(duì)列中等到JS引擎空閑時(shí)立即被執(zhí)行。
當(dāng)一個(gè)事件被觸發(fā)時(shí)該線程會(huì)把事件添加到待處理隊(duì)列的隊(duì)尾,等待JS引擎的處理。這些事件可來自JavaScript引擎當(dāng)前執(zhí)行的代碼塊如setTimeOut、也可來自瀏覽器內(nèi)核的其他線程如鼠標(biāo)點(diǎn)擊、AJAX異步請(qǐng)求等,但由于JS的單線程關(guān)系所有這些事件都得排隊(duì)等待JS引擎處理。
上述內(nèi)容就是JavaScript中setTimeOut的使用方法,你們學(xué)到知識(shí)或技能了嗎?如果還想學(xué)到更多技能或者豐富自己的知識(shí)儲(chǔ)備,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。
當(dāng)前標(biāo)題:JavaScript中setTimeOut的使用方法
轉(zhuǎn)載注明:http://www.rwnh.cn/article32/ipcgsc.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供手機(jī)網(wǎng)站建設(shè)、網(wǎng)站策劃、動(dòng)態(tài)網(wǎng)站、營(yíng)銷型網(wǎng)站建設(shè)、品牌網(wǎng)站制作、用戶體驗(yàn)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)
移動(dòng)網(wǎng)站建設(shè)知識(shí)