Javascript定時器那些事兒

一、什么是定時器

JS提供了一些原生方法來實現延時去執(zhí)行某一段代碼狂魔,下面來簡單介紹一下

setTimeout: 設置一個定時器榛搔,在定時器到期后執(zhí)行一次函數或代碼段

var timeoutId = window.setTimeout(func[, delay, param1, param2, ...]);
var timeoutId = window.setTimeout(code[, delay]);
  • timeoutId: 定時器ID
  • func: 延遲后執(zhí)行的函數
  • code: 延遲后執(zhí)行的代碼字符串,不推薦使用原理類似eval()
  • delay: 延遲的時間(單位:毫秒)变屁,默認值為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: 在瀏覽器完全結束當前運行的操作之后立即執(zhí)行指定的函數(僅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的定時器闷板,而本文將會主要介紹比較常用的兩種:setTimeoutsetInterval澎灸。

二、舉個栗子

  • 基本用法
// 下面代碼執(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
  • setIntervalsetTimeout的區(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);

如果是setTimeoutsetInterval的話,它倆僅僅在執(zhí)行次數上有區(qū)別括蝠,setTimeout一次、setIntervaln次饭聚。

而通過setTimeout模擬的setIntervalsetInterval的區(qū)別則在于:setTimeout只有在回調完成之后才會去調用下一次定時器忌警,而setInterval則不管回調函數的執(zhí)行情況,當到達規(guī)定時間就會在事件隊列中插入一個執(zhí)行回調的事件秒梳,所以在選擇定時器的方式時需要考慮setInterval的這種特性是否會對你的業(yè)務代碼有什么影響法绵?

  • 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ā)現setTimeout更早執(zhí)行

  • 面試題

下面代碼運行后的結果是什么朋譬?

// 題目一
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中的例子來解釋定時器的工作原理徙赢,該圖為一個簡單版的原理圖。

定時器

上圖中探越,左側數字代表時間狡赐,單位毫秒;左側文字代表某一個操作完成后钦幔,瀏覽器去詢問當前隊列中存在哪些正在等待執(zhí)行的操作枕屉;藍色方塊表示正在執(zhí)行的代碼塊;右側文字代表在代碼運行過程中鲤氢,出現哪些異步事件搀擂。該圖大致流程如下:

  • 程序開始時,有一個JS代碼塊開始執(zhí)行卷玉,執(zhí)行時長約為18ms哨颂,在執(zhí)行過程中有3個異步事件觸發(fā),其中包括一個setTimeout揍庄、鼠標點擊事件咆蒿、setInterval
  • 第一個setTimeout先運行,延遲時間為10ms,稍后鼠標事件出現沃测,瀏覽器在事件隊列中插入點擊的回調函數缭黔,稍后setInterval運行,10ms到達之后蒂破,setTimeout向事件隊列中插入setTimeout的回調
  • 當第一個代碼塊執(zhí)行完成后馏谨,瀏覽器查看隊列中有哪些事件在等待,他取出排在隊列最前面的代碼來執(zhí)行
  • 在瀏覽器處理鼠標點擊回調時附迷,setInterval再次檢查到到達延遲時間惧互,他將再次向事件隊列中插入一個interval的回調,以后每隔指定的延遲時間之后都會向隊列中插入一個回調
  • 后面瀏覽器將在執(zhí)行完當前隊頭的代碼之后喇伯,將再次取出目前隊頭的事件來執(zhí)行

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

四稻据、題目答案

好啦艾猜,我們現在再來看看上面的面試題的答案。

第一題

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

第二題

代碼會輸出 5 5 5 5 5算柳,理由同上,當i = 0時姓言,生成一個定時器瞬项,將回調插入到事件隊列中,等待當前隊列中無任務執(zhí)行時立即執(zhí)行事期,而此時for循環(huán)正在執(zhí)行滥壕,所以回調被擱置纸颜。當for循環(huán)執(zhí)行完成后兽泣,隊列中存在著5個回調函數,他們的都將執(zhí)行console.log(i)的操作胁孙,因為當前JS代碼上中并沒有使用塊級作用域唠倦,所以i的值在for循環(huán)結束后一直為5,所以代碼將輸出5個5

第三題

這個問題涉及到this的指向問題涮较,由setTimeout()調用的代碼運行在與所在函數完全分離的執(zhí)行環(huán)境上. 這會導致這些代碼中包含的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引擎只有一個線程周霉,所以它將會強制異步事件排隊執(zhí)行
  • 如果setInterval的回調執(zhí)行時間長于指定的延遲,setInterval將無間隔的一個接一個執(zhí)行
  • this的指向問題可以通過bind函數亚皂、定義變量俱箱、箭頭函數的方式來解決

六、參考

博客地址: ssh.today灭必,歡迎關注

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末狞谱,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子禁漓,更是在濱河造成了極大的恐慌跟衅,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件播歼,死亡現場離奇詭異与斤,居然都是意外死亡,警方通過查閱死者的電腦和手機荚恶,發(fā)現死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進店門撩穿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人谒撼,你說我怎么就攤上這事食寡。” “怎么了廓潜?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵抵皱,是天一觀的道長。 經常有香客問我辩蛋,道長呻畸,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任悼院,我火速辦了婚禮伤为,結果婚禮上,老公的妹妹穿的比我還像新娘据途。我一直安慰自己绞愚,他們只是感情好,可當我...
    茶點故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布颖医。 她就那樣靜靜地躺著位衩,像睡著了一般。 火紅的嫁衣襯著肌膚如雪熔萧。 梳的紋絲不亂的頭發(fā)上糖驴,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天僚祷,我揣著相機與錄音,去河邊找鬼贮缕。 笑死久妆,一個胖子當著我的面吹牛,可吹牛的內容都是我干的跷睦。 我是一名探鬼主播筷弦,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼抑诸!你這毒婦竟也來了烂琴?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤蜕乡,失蹤者是張志新(化名)和其女友劉穎奸绷,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體层玲,經...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡号醉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了辛块。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片畔派。...
    茶點故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖润绵,靈堂內的尸體忽然破棺而出线椰,到底是詐尸還是另有隱情,我是刑警寧澤尘盼,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布憨愉,位于F島的核電站,受9級特大地震影響卿捎,放射性物質發(fā)生泄漏配紫。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一午阵、第九天 我趴在偏房一處隱蔽的房頂上張望躺孝。 院中可真熱鬧,春花似錦趟庄、人聲如沸括细。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至锉试,卻和暖如春猫十,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工拖云, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留贷笛,地道東北人。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓宙项,卻偏偏與公主長得像乏苦,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子尤筐,可洞房花燭夜當晚...
    茶點故事閱讀 42,828評論 2 345

推薦閱讀更多精彩內容

  • JavaScript提供定時執(zhí)行代碼的功能汇荐,叫做定時器(timer),主要由setTimeout()和setInt...
    許先生__閱讀 567評論 0 1
  • 一盆繁、JavaScript單線程模型 JavaScript是單線程的掀淘,JavaScript只在一個線程上運行,但是瀏...
    Brolly閱讀 1,132評論 4 6
  • JavaScript 提供定時執(zhí)行代碼的功能油昂,叫做定時器(timer)革娄,主要由setTimeout()和setIn...
    oWSQo閱讀 3,795評論 2 1
  • ??JavaScript 是一種極其靈活的語言拦惋,具有多種使用風格。 ??一般來說安寺,編寫 JavaScript 要么...
    霜天曉閱讀 738評論 0 0
  • JavaScript提供定時執(zhí)行代碼的功能架忌,叫做定時器(timer),主要由setTimeout()和setInt...
    晚晴幽草閱讀 1,643評論 1 18