1. 河豚號 > 生活百科 >

js的定時器有哪些(定時器的4種寫法及介紹)

JS提供了一些原生方法來實現(xiàn)延時去執(zhí)行某一段代碼,下面來簡單介紹一下setTiemout、setInterval、setImmediate、requestAnimationFrame。

一、什么是定時器

JS提供了一些原生方法來實現(xiàn)延時去執(zhí)行某一段代碼,下面來簡單介紹一下 setTimeout: 設(shè)置一個定時器,在定時器到期后執(zhí)行一次函數(shù)或代碼段

var timeoutId = window.setTimeout(func[, delay, param1, param2, ...]);

var timeoutId = window.setTimeout(code[, delay]);

timeoutId: 定時器IDfunc: 延遲后執(zhí)行的函數(shù)code: 延遲后執(zhí)行的代碼字符串,不推薦使用原理類似eval()delay: 延遲的時間(單位:毫秒),默認值為0param1,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ù)操作的IDfunc: 延遲調(diào)用的函數(shù)code: 代碼段delay: 延遲時間,沒有默認值

setImmediate: 在瀏覽器完全結(jié)束當(dāng)前運行的操作之后立即執(zhí)行指定的函數(shù)(僅IE10和Node 0.10+中有實現(xiàn)),類似setTimeout(func, 0)

var immediateId = setImmediate(func[, param1, param2, ...]);

var immediateId = setImmediate(func);

immediateId: 定時器IDfunc: 回調(diào)

requestAnimationFrame: 專門為實現(xiàn)高性能的幀動畫而設(shè)計的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)?shù)竭_規(guī)定時間就會在事件隊列中插入一個執(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í)行

面試題

下面代碼運行后的結(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中的例子來解釋定時器的工作原理,該圖為一個簡單版的原理圖。

JS 定時器的4種寫法及介紹

 

Timers

上圖中,左側(cè)數(shù)字代表時間,單位毫秒;左側(cè)文字代表某一個操作完成后,瀏覽器去詢問當(dāng)前隊列中存在哪些正在等待執(zhí)行的操作;藍色方塊表示正在執(zhí)行的代碼塊;右側(cè)文字代表在代碼運行過程中,出現(xiàn)哪些異步事件。該圖大致流程如下:

程序開始時,有一個JS代碼塊開始執(zhí)行,執(zhí)行時長約為18ms,在執(zhí)行過程中有3個異步事件觸發(fā),其中包括一個setTimeout、鼠標(biāo)點擊事件、setInterval

第一個setTimeout先運行,延遲時間為10ms,稍后鼠標(biāo)事件出現(xiàn),瀏覽器在事件隊列中插入點擊的回調(diào)函數(shù),稍后setInterval運行,10ms到達之后,setTimeout向事件隊列中插入setTimeout的回調(diào)

當(dāng)?shù)谝粋€代碼塊執(zhí)行完成后,瀏覽器查看隊列中有哪些事件在等待,他取出排在隊列最前面的代碼來執(zhí)行

在瀏覽器處理鼠標(biāo)點擊回調(diào)時,setInterval再次檢查到到達延遲時間,他將再次向事件隊列中插入一個interval的回調(diào),以后每隔指定的延遲時間之后都會向隊列中插入一個回調(diào)

后面瀏覽器將在執(zhí)行完當(dāng)前隊頭的代碼之后,將再次取出目前隊頭的事件來執(zhí)行

這里只是對定時器的原理做一個簡單版的描述,實際的處理過程比這個復(fù)雜。

四、題目答案

好啦,我們現(xiàn)在再來看看上面的面試題的答案。

第一題

alert永遠都不會執(zhí)行,因為JS是單線程的,且定時器的回調(diào)將在等待當(dāng)前正在執(zhí)行的任務(wù)完成后才執(zhí)行,而while(t) {}直接就進入了死循環(huán)一直占用線程,不給回調(diào)函數(shù)執(zhí)行機會

第二題

代碼會輸出 5 5 5 5 5,理由同上,當(dāng)i = 0時,生成一個定時器,將回調(diào)插入到事件隊列中,等待當(dāng)前隊列中無任務(wù)執(zhí)行時立即執(zhí)行,而此時for循環(huán)正在執(zhí)行,所以回調(diào)被擱置。當(dāng)for循環(huán)執(zhí)行完成后,隊列中存在著5個回調(diào)函數(shù),他們的都將執(zhí)行console.log(i)的操作,因為當(dāng)前JS代碼上中并沒有使用塊級作用域,所以i的值在for循環(huán)結(jié)束后一直為5,所以代碼將輸出5個5

第三題

這個問題涉及到this的指向問題,由setTimeout()調(diào)用的代碼運行在與所在函數(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();

五、需要注意的點

setTimeout有最小時間間隔限制,HTML5標(biāo)準為4ms,小于4ms按照4ms處理,但是每個瀏覽器實現(xiàn)的最小間隔都不同

因為JS引擎只有一個線程,所以它將會強制異步事件排隊執(zhí)行

如果setInterval的回調(diào)執(zhí)行時間長于指定的延遲,setInterval將無間隔的一個接一個執(zhí)行

this的指向問題可以通過bind函數(shù)、定義變量、箭頭函數(shù)的方式來解決

本文由網(wǎng)上采集發(fā)布,不代表我們立場,轉(zhuǎn)載聯(lián)系作者并注明出處:http://m.zltfw.cn/shbk/39377.html

聯(lián)系我們

在線咨詢:點擊這里給我發(fā)消息

微信號:15705946153

工作日:9:30-18:30,節(jié)假日休息