文章插圖

文章插圖
JS提供了一些原生方法來實現延時去執行某一段代碼 , 下面來簡單介紹一下setTiemout、setInterval、setImmediate、requestAnimationFrame 。
一、什么是定時器
JS提供了一些原生方法來實現延時去執行某一段代碼 , 下面來簡單介紹一下 setTimeout: 設置一個定時器 , 在定時器到期后執行一次函數或代碼段
var timeoutId = window.setTimeout(func[, delay, param1, param2, ...]);var timeoutId = window.setTimeout(code[, delay]);timeoutId: 定時器IDfunc: 延遲后執行的函數code: 延遲后執行的代碼字符串 , 不推薦使用原理類似eval()delay: 延遲的時間(單位:毫秒) , 默認值為0param1,param2: 向延遲函數傳遞而外的參數 , IE9以上支持setInterval: 以固定的時間間隔重復調用一個函數或者代碼段
var intervalId = window.setInterval(func, delay[, param1, param2, ...]);var intervalId = window.setInterval(code, delay);intervalId: 重復操作的IDfunc: 延遲調用的函數code: 代碼段delay: 延遲時間 , 沒有默認值【js中的定時器有哪些 js定時器有哪些,區別及用法】setImmediate: 在瀏覽器完全結束當前運行的操作之后立即執行指定的函數(僅IE10和Node 0.10+中有實現) , 類似setTimeout(func, 0)
var immediateId = setImmediate(func[, param1, param2, ...]);var immediateId = setImmediate(func);immediateId: 定時器IDfunc: 回調requestAnimationFrame: 專門為實現高性能的幀動畫而設計的API , 但是不能指定延遲時間 , 而是根據瀏覽器的刷新頻率而定(幀)
var requestId = window.requestAnimationFrame(func);
func: 回調上面簡單的介紹了四種JS的定時器 , 而本文將會主要介紹比較常用的兩種:setTimeout和setInterval 。
二、舉個栗子基本用法
// 下面代碼執行之后會輸出什么?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、5setInterval 和 setTimeout的區別?// 執行在面的代碼塊會輸出什么?setTimeout(function () {console.log('timeout');}, 1000);setInterval(function () {console.log('interval')}, 1000);// 輸出一次 timeout , 每隔1S輸出一次 interval/*--------------------------------*/// 通過setTimeout模擬setInterval 和 setInterval有啥區別么?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的話 , 它倆僅僅在執行次數上有區別 , setTimeout一次、setIntervaln次 。而通過setTimeout模擬的setInterval與setInterval的區別則在于:setTimeout只有在回調完成之后才會去調用下一次定時器 , 而setInterval則不管回調函數的執行情況 , 當到達規定時間就會在事件隊列中插入一個執行回調的事件 , 所以在選擇定時器的方式時需要考慮setInterval的這種特性是否會對你的業務代碼有什么影響?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中測試發現setTimeout更早執行面試題
下面代碼運行后的結果是什么?
// 題目一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中的例子來解釋定時器的工作原理 , 該圖為一個簡單版的原理圖 。
Timers
上圖中 , 左側數字代表時間 , 單位毫秒;左側文字代表某一個操作完成后 , 瀏覽器去詢問當前隊列中存在哪些正在等待執行的操作;藍色方塊表示正在執行的代碼塊;右側文字代表在代碼運行過程中 , 出現哪些異步事件 。該圖大致流程如下:
程序開始時 , 有一個JS代碼塊開始執行 , 執行時長約為18ms , 在執行過程中有3個異步事件觸發 , 其中包括一個setTimeout、鼠標點擊事件、setInterval第一個setTimeout先運行 , 延遲時間為10ms , 稍后鼠標事件出現 , 瀏覽器在事件隊列中插入點擊的回調函數 , 稍后setInterval運行 , 10ms到達之后 , setTimeout向事件隊列中插入setTimeout的回調當第一個代碼塊執行完成后 , 瀏覽器查看隊列中有哪些事件在等待 , 他取出排在隊列最前面的代碼來執行在瀏覽器處理鼠標點擊回調時 , setInterval再次檢查到到達延遲時間 , 他將再次向事件隊列中插入一個interval的回調 , 以后每隔指定的延遲時間之后都會向隊列中插入一個回調后面瀏覽器將在執行完當前隊頭的代碼之后 , 將再次取出目前隊頭的事件來執行
這里只是對定時器的原理做一個簡單版的描述 , 實際的處理過程比這個復雜 。
四、題目答案
好啦 , 我們現在再來看看上面的面試題的答案 。第一題
alert永遠都不會執行 , 因為JS是單線程的 , 且定時器的回調將在等待當前正在執行的任務完成后才執行 , 而while(t) {}直接就進入了死循環一直占用線程 , 不給回調函數執行機會第二題
代碼會輸出 5 5 5 5 5 , 理由同上 , 當i = 0時 , 生成一個定時器 , 將回調插入到事件隊列中 , 等待當前隊列中無任務執行時立即執行 , 而此時for循環正在執行 , 所以回調被擱置 。當for循環執行完成后 , 隊列中存在著5個回調函數 , 他們的都將執行console.log(i)的操作 , 因為當前JS代碼上中并沒有使用塊級作用域 , 所以i的值在for循環結束后一直為5 , 所以代碼將輸出5個5第三題
這個問題涉及到this的指向問題 , 由setTimeout()調用的代碼運行在與所在函數完全分離的執行環境上. 這會導致這些代碼中包含的this關鍵字會指向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();五、需要注意的點setTimeout有最小時間間隔限制 , HTML5標準為4ms , 小于4ms按照4ms處理 , 但是每個瀏覽器實現的最小間隔都不同因為JS引擎只有一個線程 , 所以它將會強制異步事件排隊執行如果setInterval的回調執行時間長于指定的延遲 , setInterval將無間隔的一個接一個執行this的指向問題可以通過bind函數、定義變量、箭頭函數的方式來解決- 視頻播放器都有啥 視頻播放器有哪幾種
- 德惠特色美食有哪些
- iphone4s有8g的嗎 8g的iphone4s夠用嗎
- 月亮月亮真有趣,最愛變著戲法玩 月亮月亮真有趣
- 有趣的手工折紙 最酷的折紙
- iphone 4和4s有什么區別 iPhone四和iPhone 4S有什么區別
- 青春有你2戀愛循環有版權嗎,戀愛循環不播了是真的嗎
- 春筍有點苦有毒嗎,春筍苦味兒去除方法
- 青春有你2林凡是男是女,青春有你2陸柯燃和林凡什么關系
- 一般會有4個征兆。 女人可以挽回的征兆
