摘要:一什么是定時器提供了一些原生方法來實(shí)現(xiàn)延時去執(zhí)行某一段代碼,下面來簡單介紹一下設(shè)置一個定時器,在定時器到期后執(zhí)行一次函數(shù)或代碼段定時器延遲后執(zhí)行的函數(shù)延遲后執(zhí)行的代碼字符串,不推薦使用原理類似延遲的時間單位毫秒,默認(rèn)值為向延遲函數(shù)傳遞而外的
一、什么是定時器
JS提供了一些原生方法來實(shí)現(xiàn)延時去執(zhí)行某一段代碼,下面來簡單介紹一下
setTimeout: 設(shè)置一個定時器,在定時器到期后執(zhí)行一次函數(shù)或代碼段
var timeoutId = window.setTimeout(func[, delay, param1, param2, ...]); var timeoutId = window.setTimeout(code[, delay]);
timeoutId: 定時器ID
func: 延遲后執(zhí)行的函數(shù)
code: 延遲后執(zhí)行的代碼字符串,不推薦使用原理類似eval()
delay: 延遲的時間(單位:毫秒),默認(rèn)值為0
param1,param2: 向延遲函數(shù)傳遞而外的參數(shù),IE9以上支持
setInterval: 以固定的時間間隔重復(fù)調(diào)用一個函數(shù)或者代碼段
var intervalId = window.setInterval(func, delay[, param1, param2, ...]); var intervalId = window.setInterval(code, delay);
intervalId: 重復(fù)操作的ID
func: 延遲調(diào)用的函數(shù)
code: 代碼段
delay: 延遲時間,沒有默認(rèn)值
setImmediate: 在瀏覽器完全結(jié)束當(dāng)前運(yùn)行的操作之后立即執(zhí)行指定的函數(shù)(僅IE10和Node 0.10+中有實(shí)現(xiàn)),類似setTimeout(func, 0)
var immediateId = setImmediate(func[, param1, param2, ...]); var immediateId = setImmediate(func);
immediateId: 定時器ID
func: 回調(diào)
requestAnimationFrame: 專門為實(shí)現(xiàn)高性能的幀動畫而設(shè)計(jì)的API,但是不能指定延遲時間,而是根據(jù)瀏覽器的刷新頻率而定(幀)
var requestId = window.requestAnimationFrame(func);
func: 回調(diào)
上面簡單的介紹了四種JS的定時器,而本文將會主要介紹比較常用的兩種:setTimeout和setInterval。
二、舉個栗子基本用法
// 下面代碼執(zhí)行之后會輸出什么? var intervalId, timeoutId; timeoutId = setTimeout(function () { console.log(1); }, 300); setTimeout(function () { clearTimeout(timeoutId); console.log(2); }, 100); setTimeout("console.log("5")", 400); intervalId = setInterval(function () { console.log(4); clearInterval(intervalId); }, 200); // 分別輸出: 2、4、5
setInterval 和 setTimeout的區(qū)別?
// 執(zhí)行在面的代碼塊會輸出什么? setTimeout(function () { console.log("timeout"); }, 1000); setInterval(function () { console.log("interval") }, 1000); // 輸出一次 timeout,每隔1S輸出一次 interval /*--------------------------------*/ // 通過setTimeout模擬setInterval 和 setInterval有啥區(qū)別么? var callback = function () { if (times++ > max) { clearTimeout(timeoutId); clearInterval(intervalId); } console.log("start", Date.now() - start); for (var i = 0; i < 990000000; i++) {} console.log("end", Date.now() - start); }, delay = 100, times = 0, max = 5, start = Date.now(), intervalId, timeoutId; function imitateInterval(fn, delay) { timeoutId = setTimeout(function () { fn(); if (times <= max) { imitateInterval(fn ,delay); } }, delay); } imitateInterval(callback, delay); intervalId = setInterval(callback, delay);
如果是setTimeout和setInterval的話,它倆僅僅在執(zhí)行次數(shù)上有區(qū)別,setTimeout一次、setIntervaln次。
而通過setTimeout模擬的setInterval與setInterval的區(qū)別則在于:setTimeout只有在回調(diào)完成之后才會去調(diào)用下一次定時器,而setInterval則不管回調(diào)函數(shù)的執(zhí)行情況,當(dāng)到達(dá)規(guī)定時間就會在事件隊(duì)列中插入一個執(zhí)行回調(diào)的事件,所以在選擇定時器的方式時需要考慮setInterval的這種特性是否會對你的業(yè)務(wù)代碼有什么影響?
setTimeout(func, 0) 和 setImmediate(func)誰更快?(僅僅是好奇,才寫的這段測試)
console.time("immediate"); console.time("timeout"); setImmediate(() => { console.timeEnd("immediate"); }); setTimeout(() => { console.timeEnd("timeout"); }, 0);
在Node.JS v6.7.0中測試發(fā)現(xiàn)setTimeout更早執(zhí)行
面試題
下面代碼運(yùn)行后的結(jié)果是什么?
// 題目一 var t = true; setTimeout(function(){ t = false; }, 1000); while(t){} alert("end"); /*--------------------------------*/ // 題目二 for (var i = 0; i < 5; i++) { setTimeout(function () { console.log(i); }, 0); } /*--------------------------------*/ // 題目三 var obj = { msg: "obj", shout: function () { alert(this.msg); }, waitAndShout: function() { setTimeout(function () { this.shout(); }, 0); } }; obj.waitAndShout();
問題答案會在后面解答
三、JS定時器的工作原理在解釋上面問題的答案之前我們先來了解一下定時器的工作原理,這里將用引用How JavaScript Timers Work中的例子來解釋定時器的工作原理,該圖為一個簡單版的原理圖。
上圖中,左側(cè)數(shù)字代表時間,單位毫秒;左側(cè)文字代表某一個操作完成后,瀏覽器去詢問當(dāng)前隊(duì)列中存在哪些正在等待執(zhí)行的操作;藍(lán)色方塊表示正在執(zhí)行的代碼塊;右側(cè)文字代表在代碼運(yùn)行過程中,出現(xiàn)哪些異步事件。該圖大致流程如下:
程序開始時,有一個JS代碼塊開始執(zhí)行,執(zhí)行時長約為18ms,在執(zhí)行過程中有3個異步事件觸發(fā),其中包括一個setTimeout、鼠標(biāo)點(diǎn)擊事件、setInterval
第一個setTimeout先運(yùn)行,延遲時間為10ms,稍后鼠標(biāo)事件出現(xiàn),瀏覽器在事件隊(duì)列中插入點(diǎn)擊的回調(diào)函數(shù),稍后setInterval運(yùn)行,10ms到達(dá)之后,setTimeout向事件隊(duì)列中插入setTimeout的回調(diào)
當(dāng)?shù)谝粋€代碼塊執(zhí)行完成后,瀏覽器查看隊(duì)列中有哪些事件在等待,他取出排在隊(duì)列最前面的代碼來執(zhí)行
在瀏覽器處理鼠標(biāo)點(diǎn)擊回調(diào)時,setInterval再次檢查到到達(dá)延遲時間,他將再次向事件隊(duì)列中插入一個interval的回調(diào),以后每隔指定的延遲時間之后都會向隊(duì)列中插入一個回調(diào)
后面瀏覽器將在執(zhí)行完當(dāng)前隊(duì)頭的代碼之后,將再次取出目前隊(duì)頭的事件來執(zhí)行
這里只是對定時器的原理做一個簡單版的描述,實(shí)際的處理過程比這個復(fù)雜。
四、題目答案好啦,我們現(xiàn)在再來看看上面的面試題的答案。
第一題
alert永遠(yuǎn)都不會執(zhí)行,因?yàn)镴S是單線程的,且定時器的回調(diào)將在等待當(dāng)前正在執(zhí)行的任務(wù)完成后才執(zhí)行,而while(t) {}直接就進(jìn)入了死循環(huán)一直占用線程,不給回調(diào)函數(shù)執(zhí)行機(jī)會
第二題
代碼會輸出 5 5 5 5 5,理由同上,當(dāng)i = 0時,生成一個定時器,將回調(diào)插入到事件隊(duì)列中,等待當(dāng)前隊(duì)列中無任務(wù)執(zhí)行時立即執(zhí)行,而此時for循環(huán)正在執(zhí)行,所以回調(diào)被擱置。當(dāng)for循環(huán)執(zhí)行完成后,隊(duì)列中存在著5個回調(diào)函數(shù),他們的都將執(zhí)行console.log(i)的操作,因?yàn)楫?dāng)前js代碼上中并沒有使用塊級作用域,所以i的值在for循環(huán)結(jié)束后一直為5,所以代碼將輸出5個5
第三題
這個問題涉及到this的指向問題,由setTimeout()調(diào)用的代碼運(yùn)行在與所在函數(shù)完全分離的執(zhí)行環(huán)境上. 這會導(dǎo)致這些代碼中包含的this關(guān)鍵字會指向window (或全局)對象,window對象中并不存在shout方法,所以就會報錯,修改方案如下:
var obj = { msg: "obj", shout: function () { alert(this.msg); }, waitAndShout: function() { var self = this; // 這里將this賦給一個變量 setTimeout(function () { self.shout(); }, 0); } }; obj.waitAndShout();五、需要注意的點(diǎn)
setTimeout有最小時間間隔限制,HTML5標(biāo)準(zhǔn)為4ms,小于4ms按照4ms處理,但是每個瀏覽器實(shí)現(xiàn)的最小間隔都不同
因?yàn)镴S引擎只有一個線程,所以它將會強(qiáng)制異步事件排隊(duì)執(zhí)行
如果setInterval的回調(diào)執(zhí)行時間長于指定的延遲,setInterval將無間隔的一個接一個執(zhí)行
this的指向問題可以通過bind函數(shù)、定義變量、箭頭函數(shù)的方式來解決
六、參考MDN
How JavaScript Timers Work
JavaScript定時器與執(zhí)行機(jī)制解析
博客地址: ssh.today,歡迎關(guān)注
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/88180.html
摘要:消息隊(duì)列和事件循環(huán)異步過程中,工作線程在異步操作完成后需要通知主線程,那么這個通知機(jī)制是怎樣實(shí)現(xiàn)的呢答案是利用消息隊(duì)列和事件循環(huán)。 Event Loop 那些事兒 我們通常說 JavaScript 是單線程的,實(shí)際上是指在 JS 引擎中負(fù)責(zé)解釋和執(zhí)行 JS 代碼的線程只有一個,一般成為主線程,在這種前提下,為了讓用戶的操作不存在阻塞感,前端 APP 的運(yùn)行需要依賴于大量的異步過程,所以...
摘要:簡介是對象表示法的縮寫是一種數(shù)據(jù)格式而不是一種編程語言用來表示結(jié)構(gòu)化數(shù)據(jù)是的一個嚴(yán)格子集并不從屬于很多編程語言都可以用數(shù)據(jù)格式語法語法可以表示以下三種類型的值簡單值字符串?dāng)?shù)值布爾值但是不支持對象數(shù)組不支持變量函數(shù)對象實(shí)例簡單值字符串?dāng)?shù)字布爾 JSON 簡介 JSON是JavaScript Object Notation(JavaScript對象表示法)的縮寫 JSON是一種數(shù)據(jù)格式,...
閱讀 2500·2021-11-17 09:33
閱讀 796·2021-11-04 16:13
閱讀 1358·2021-10-14 09:50
閱讀 718·2019-08-30 15:53
閱讀 3693·2019-08-30 14:18
閱讀 3287·2019-08-30 14:14
閱讀 2127·2019-08-30 12:46
閱讀 3207·2019-08-26 14:05