JS提供了一些原生方法來實現延時去執行某一段代碼,下面來簡單介紹一下javascript
setTimeout
: 設置一個定時器,在定時器到期後執行一次函數或代碼段java
var timeoutId = window.setTimeout(func[, delay, param1, param2, ...]); var timeoutId = window.setTimeout(code[, delay]);
timeoutId: 定時器ID面試
func: 延遲後執行的函數瀏覽器
code: 延遲後執行的代碼字符串,不推薦使用原理相似
eval()
sshdelay: 延遲的時間(單位:毫秒),默認值爲0異步
param1,param2: 向延遲函數傳遞而外的參數,IE9以上支持函數
setInterval
: 以固定的時間間隔重複調用一個函數或者代碼段性能
var intervalId = window.setInterval(func, delay[, param1, param2, ...]); var intervalId = window.setInterval(code, delay);
intervalId: 重複操做的ID測試
func: 延遲調用的函數動畫
code: 代碼段
delay: 延遲時間,沒有默認值
setImmediate
: 在瀏覽器徹底結束當前運行的操做以後當即執行指定的函數(僅IE10和Node 0.10+中有實現),相似setTimeout(func, 0)
var immediateId = setImmediate(func[, param1, param2, ...]); var immediateId = setImmediate(func);
immediateId: 定時器ID
func: 回調
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); // 分別輸出: 二、四、5
setInterval
和 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
一次、setInterval
n次。
而經過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();
問題答案會在後面解答
在解釋上面問題的答案以前咱們先來了解一下定時器的工做原理,這裏將用引用How JavaScript Timers Work中的例子來解釋定時器的工做原理,該圖爲一個簡單版的原理圖。
上圖中,左側數字表明時間,單位毫秒;左側文字表明某一個操做完成後,瀏覽器去詢問當前隊列中存在哪些正在等待執行的操做;藍色方塊表示正在執行的代碼塊;右側文字表明在代碼運行過程當中,出現哪些異步事件。該圖大體流程以下:
程序開始時,有一個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
函數、定義變量、箭頭函數的方式來解決
博客地址: ssh.today,歡迎關注