前端碎碎念 之 nextTick, setTimeout 以及 setImmediate 三者的執(zhí)行順序

轉(zhuǎn)載自https://segmentfault.com/a/1190000008595101

『前端碎碎念』系列會記錄我平時看書或者看文章遇到的問題青瀑,一般都是比較基礎但是容易遺忘的知識點瀑粥,你也可能會在面試中碰到埋酬。 我會查閱一些資料并可能加上自己的理解,來記錄這些問題柱告。更多文章請前往我的個人博客

這個問題是有關執(zhí)行順序和Event Loop的掠廓。關于Event Loop和任務隊列等概念亏狰,可以先閱讀我引用中的文章,本文主要分析一些存在的疑惑點每瞒。

下面這個例子比較典型:

setImmediate(function(){console.log(1);},0);setTimeout(function(){console.log(2);},0);newPromise(function(resolve){console.log(3);? ? resolve();console.log(4);}).then(function(){console.log(5);});console.log(6);process.nextTick(function(){console.log(7);});console.log(8);//輸出結(jié)果是3 4 6 8 7 5 2 1

在解釋輸出結(jié)果之前金闽,我們來看幾個概念:

macro-task: script (整體代碼),setTimeout, setInterval, setImmediate, I/O, UI rendering.

micro-task: process.nextTick, Promise(原生)剿骨,Object.observe代芜,MutationObserver

除了script整體代碼,micro-task的任務優(yōu)先級高于macro-task的任務優(yōu)先級浓利。其中挤庇,script(整體代碼) 钞速,可以理解為待執(zhí)行的所有代碼。

所以執(zhí)行順序如下:

第一步. script整體代碼被執(zhí)行罚随,執(zhí)行過程為

創(chuàng)建setImmediate macro-task

創(chuàng)建setTimeout macro-task

創(chuàng)建micro-task Promise.then 的回調(diào)玉工,并執(zhí)行script console.log(3); resolve(); console.log(4); 此時輸出3和4,雖然resolve調(diào)用了淘菩,執(zhí)行了但是整體代碼還沒執(zhí)行完遵班,無法進入Promise.then 流程。

console.log(6)輸出6

process.nextTick 創(chuàng)建micro-task

console.log(8) 輸出8

第一個過程過后潮改,已經(jīng)輸出了3 4 6 8

第二步. 由于其他micro-task 的 優(yōu)先級高于macro-task狭郑。

此時micro-task 中有兩個任務按照優(yōu)先級process.nextTick 高于 Promise。

所以先輸出7汇在,再輸出5

第三步翰萨,micro-task 任務列表已經(jīng)執(zhí)行完畢,家下來執(zhí)行macro-task. 由于setTimeout的優(yōu)先級高于setIImmediate糕殉,所以先輸出2亩鬼,再輸出1。

整個過程描述起來像是同步操作阿蝶,實際上是基于Event Loop的事件循環(huán)雳锋。

關于micro-task和macro-task的執(zhí)行順序,可看下面這個例子(來自《深入淺出Node.js》):

//加入兩個nextTick的回調(diào)函數(shù)process.nextTick(function(){console.log('nextTick延遲執(zhí)行1');});process.nextTick(function(){console.log('nextTick延遲執(zhí)行2');});// 加入兩個setImmediate()的回調(diào)函數(shù)setImmediate(function(){console.log('setImmediate延遲執(zhí)行1');// 進入下次循環(huán)process.nextTick(function(){console.log('強勢插入');? ? });});setImmediate(function(){console.log('setImmediate延遲執(zhí)行2'); });console.log('正常執(zhí)行');

書中給出的執(zhí)行結(jié)果是:

正常執(zhí)行nextTick延遲執(zhí)行1nextTick延遲執(zhí)行2setImmediate延遲執(zhí)行1強勢插入setImmediate延遲執(zhí)行2

process.nextTick在兩個setImmediate之間強行插入了羡洁。但運行這段代碼發(fā)現(xiàn)結(jié)果卻是這樣:

正常執(zhí)行nextTick延遲執(zhí)行1nextTick延遲執(zhí)行2setImmediate延遲執(zhí)行1setImmediate延遲執(zhí)行2強勢插入

樸老師寫那本書的時候玷过,node最新版本為0.10.13,而我的版本是6.x

老版本的Node會優(yōu)先執(zhí)行process.nextTick筑煮。當process.nextTick隊列執(zhí)行完后再執(zhí)行一個setImmediate任務辛蚊。然后再次回到新的事件循環(huán)。所以執(zhí)行完第一個setImmediate后真仲,隊列里只剩下第一個setImmediate里的process.nextTick和第二個setImmediate袋马。所以process.nextTick會先執(zhí)行。

而在新版的Node中秸应,process.nextTick執(zhí)行完后虑凛,會循環(huán)遍歷setImmediate,將setImmediate都執(zhí)行完畢后再跳出循環(huán)灸眼。所以兩個setImmediate執(zhí)行完后隊列里只剩下第一個setImmediate里的process.nextTick卧檐。最后輸出"強勢插入"。

具體實現(xiàn)可參考Node.js源碼焰宣。

關于優(yōu)先級的另一個比較清晰的版本:

觀察者優(yōu)先級

在每次輪訓檢查中霉囚,各觀察者的優(yōu)先級分別是:

idle觀察者 > I/O觀察者 > check觀察者。

idle觀察者:process.nextTick

I/O觀察者:一般性的I/O回調(diào)匕积,如網(wǎng)絡盈罐,文件榜跌,數(shù)據(jù)庫I/O等

check觀察者:setImmediate,setTimeout

setImmediate 和 setTimeout 的優(yōu)先級

看下面這個例子:

setImmediate(function(){console.log('1'); });setTimeout(function(){console.log('2'); },0);console.log('3');//輸出結(jié)果是3 2 1

我們知道現(xiàn)在HTML5規(guī)定setTimeout的最小間隔時間是4ms盅粪,也就是說0實際上也會別默認設置為最小值4ms钓葫。我們把這個延遲加大

上面說到setTimeout 的優(yōu)先級比 setImmediate的高,其實這種說法是有條件的票顾。

再看下面這個例子础浮,為setTimeout增加了一個延遲20ms的時間:

setImmediate(function(){console.log('1'); });setTimeout(function(){console.log('2'); },20);console.log('3');//輸出結(jié)果是3 2 1

setTimeout延遲20ms再執(zhí)行,而setImmediate是立即執(zhí)行奠骄,竟然2比1還先輸出豆同??

試試打印出這個程序的執(zhí)行時間:

vart1 = +newDate();setImmediate(function(){console.log('1'); });setTimeout(function(){console.log('2'); },20);console.log('3');vart2 = +newDate();console.log('time: '+ (t2 - t1));//輸出3time:2321

程序執(zhí)行用了23ms, 也就是說含鳞,在script(整體代碼)執(zhí)行完之前影锈,setTimeout已經(jīng)過時了,所以當進入macro-task的時候setTimeout依然優(yōu)先于setImmediate執(zhí)行蝉绷。如果我們把這個值調(diào)大一點呢鸭廷?

vart1 = +newDate();setImmediate(function(){console.log('1'); });setTimeout(function(){console.log('2'); },30);console.log('3');vart2 = +newDate();console.log('time: '+ (t2 - t1));//輸出3time:2312

setImmediate早于setTimeout執(zhí)行了,因為進入macro-task 循環(huán)的時候熔吗,setTimeout的定時器還沒到辆床。

以上實驗是基于6.6.0版本Node.js測試,實際上在碰到類似這種問題的時候磁滚,最好的辦法是參考標準佛吓,并查閱源碼宵晚,不能死記概念和順序垂攘,因為標準也是會變的。包括此文也是自學總結(jié)淤刃,經(jīng)供參考晒他。

參考:

https://www.zhihu.com/questio...

https://segmentfault.com/a/11...

http://www.reibang.com/p/837b...

3月7日發(fā)布

更多

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市逸贾,隨后出現(xiàn)的幾起案子陨仅,更是在濱河造成了極大的恐慌,老刑警劉巖铝侵,帶你破解...
    沈念sama閱讀 211,817評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件灼伤,死亡現(xiàn)場離奇詭異,居然都是意外死亡咪鲜,警方通過查閱死者的電腦和手機狐赡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來疟丙,“玉大人颖侄,你說我怎么就攤上這事鸟雏。” “怎么了览祖?”我有些...
    開封第一講書人閱讀 157,354評論 0 348
  • 文/不壞的土叔 我叫張陵孝鹊,是天一觀的道長。 經(jīng)常有香客問我展蒂,道長又活,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,498評論 1 284
  • 正文 為了忘掉前任锰悼,我火速辦了婚禮皇钞,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘松捉。我一直安慰自己夹界,他們只是感情好,可當我...
    茶點故事閱讀 65,600評論 6 386
  • 文/花漫 我一把揭開白布隘世。 她就那樣靜靜地躺著可柿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪丙者。 梳的紋絲不亂的頭發(fā)上复斥,一...
    開封第一講書人閱讀 49,829評論 1 290
  • 那天,我揣著相機與錄音械媒,去河邊找鬼目锭。 笑死,一個胖子當著我的面吹牛纷捞,可吹牛的內(nèi)容都是我干的痢虹。 我是一名探鬼主播,決...
    沈念sama閱讀 38,979評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼主儡,長吁一口氣:“原來是場噩夢啊……” “哼奖唯!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起糜值,我...
    開封第一講書人閱讀 37,722評論 0 266
  • 序言:老撾萬榮一對情侶失蹤丰捷,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后寂汇,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體病往,經(jīng)...
    沈念sama閱讀 44,189評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,519評論 2 327
  • 正文 我和宋清朗相戀三年骄瓣,在試婚紗的時候發(fā)現(xiàn)自己被綠了停巷。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,654評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖叠穆,靈堂內(nèi)的尸體忽然破棺而出少漆,到底是詐尸還是另有隱情,我是刑警寧澤硼被,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布示损,位于F島的核電站,受9級特大地震影響嚷硫,放射性物質(zhì)發(fā)生泄漏检访。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,940評論 3 313
  • 文/蒙蒙 一仔掸、第九天 我趴在偏房一處隱蔽的房頂上張望脆贵。 院中可真熱鬧,春花似錦起暮、人聲如沸卖氨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽筒捺。三九已至,卻和暖如春纸厉,著一層夾襖步出監(jiān)牢的瞬間系吭,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評論 1 266
  • 我被黑心中介騙來泰國打工颗品, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留肯尺,地道東北人。 一個月前我還...
    沈念sama閱讀 46,382評論 2 360
  • 正文 我出身青樓躯枢,卻偏偏與公主長得像则吟,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子闺金,可洞房花燭夜當晚...
    茶點故事閱讀 43,543評論 2 349

推薦閱讀更多精彩內(nèi)容