setTimeout 和 setInterval 的定時時間深入研究
setInterval()
?- 間隔指定的毫秒數(shù)不停地執(zhí)行指定的代碼(一直執(zhí)行)。
setTimeout()
?- 在指定的毫秒數(shù)后執(zhí)行指定代碼(只執(zhí)行一次)。
使用setInterVal:
function doStuff(){
? ? ? ?
? ? ?? ?// 此處為需要執(zhí)行一段時間T的代碼?
}
? ? ? ? ?
? ? ?setInterVal(doStuff, 100);
下來用使用setTimeout模擬setInterval:
function tick() {
? ? ?
doStuff();
? ??
? setTimeout(tick, 100);?
}?
tick();
看下正常情況下兩者的區(qū)別:
看下正常情況下兩者的區(qū)別:

setInterval每個定時器之間的間隔是100ms,而setTimeout每隔100ms執(zhí)行一次doStuff
,所以每個定時器之間的間隔是100 + T(doStuff執(zhí)行時間為T);這個T就是本文的關(guān)鍵了。
如果T可以忽略的話,兩者的效果是基本相同的。
T <= 100時,setInterval定時器間隔100,setTimeout定時器間隔100+T。
如果T > 100,setTimeout依然如上圖,兩個定時器之間間隔100+T。 那么setInterval呢?
先看下圖:

在0ms時,定時器1開始進入宏任務(wù)隊列;100ms時,定時器1開始執(zhí)行doStuff1,隊列為空,定時器2進入隊列;200ms時,因為定時器2(doStuff1還沒執(zhí)行完)在隊列中,所以定時器3被跳過。瀏覽器不會同時創(chuàng)建兩個相同的間隔計時器。 300ms時,定時器2已經(jīng)開始執(zhí)行,隊列為空,定時器4進入隊列。以此類推~
下面我們用代碼驗證下。T設(shè)置為140ms。 我們讓定時器運行5次,按照上述理解,總運行時間應(yīng)該是:100+5*140 = 800ms。 代碼如下:
let i = 0;console.time("總時間");
function doStuff() {?
? ?console.log("delay");
? ?
dead(140);
?
??console.timeEnd("測試");
}
function dead(delay) {
?
??var start = new Date().getTime();
?
while (new Date().getTime() < start + delay);
}
let timer = setInterval(() => {
? ?i++;
??
?if (i > 4) {
? ?
?? ?clearInterval(timer);
? ?
? ?setTimeout(() => {
? ??
? ?console.timeEnd("總時間");
??
? ? ?}, 0);
??
?}
??
?console.log("interval start");
?
??console.time("測試");
??
?doStuff();
}, 100);
consle

可以看出定時器運行了5次,總時間的確為?100 + 140*5 = 800ms。
如果我們設(shè)置dead(250), 經(jīng)過測試總時間為?100 + 250 * 5 = 1350;

如果doStuff中的代碼是異步的呢?比如像我們常用promise。140ms返回結(jié)果。代碼如下:
let i = 0;
?
?console.time("總時間");
?
?function delay(i) {
? ?
? ?promise(i);
??
?}
??
?function promise(i) {
? ?
? ?return new Promise((resole,reject) => {? ??
? ? ? ?setTimeout(() => {? ??
? ? ? ?resole(i);? ?
? ? ? ?}, 140);
? ?
? ?}).then(res => {
? ??
? ? ?console.log("res", res);?
?? ?})
?
?}
?
??let timer = setInterval(() => {
? ?
?? ?i++;
? ?
?? ?if (i > 4) {? ?
?? ? ?clearInterval(timer);?
?? ? ?setTimeout(() => {? ?
? ? ? console.timeEnd("總時間");? ??
??}, 0);?
???}?
?? ?delay(i);?
?}, 100);

可以看出總時間是500ms,請求接口的異步代碼并不會阻塞定時器。這個也很好理解,定時器中的同步代碼會直接進入隊列,異步代碼注冊事件,完成后進入隊列;所以當(dāng)異步代碼注冊事件后,這個定時器就執(zhí)行完了,并不是等異步代碼回來后這個定時器才算結(jié)束,不然5次定時器的總時間就是800ms了。
#根據(jù)以上代碼效果總結(jié)
setInterval
只是在特定時間點將代碼推入隊列,如果已有定時器在隊列中,則會跳過。瀏覽器不會同時創(chuàng)建兩個相同的間隔計時器。
setInterval
設(shè)置定時時間小于函數(shù)體內(nèi)的執(zhí)行時間時候,則第一次執(zhí)行定時時間后面的真正的定時時間應(yīng)該是執(zhí)行函數(shù)體的總時間。
setInterval
中的異步代碼不會阻塞創(chuàng)建新的定時器。