JavaScript執(zhí)行機制发乔、Event Loop

一、運行機制

JavaScript是單線程運行機制宫屠。

為什么JavaScript是單線程列疗?
單線程就是說,js在同一時間只做一件事浪蹂。這要從JavaScript誕生說起抵栈,它最初是設(shè)計用來驗證表單的網(wǎng)頁腳本,并且在這么多年的發(fā)展中也一直是作為與用戶和DOM交互的介質(zhì)語言存在的坤次,所以對用戶來說古劲,用戶在一個網(wǎng)頁上同時只會做一個操作。如果js是并發(fā)的缰猴,那么如果同時操作一個DOM产艾,是會出現(xiàn)問題的。

二滑绒、工作者線程

(《js高程》第27章)
為了利用CPU多核計算能力闷堡,也為了將確實可以后臺等待的事件放在后臺執(zhí)行,js提出了工作者線程的解決方案疑故。
工作者線程是獨立于主執(zhí)行線程環(huán)境的一個子環(huán)境杠览,它除了無法操作DOM等必須單線程操作的API,其他與主執(zhí)行環(huán)境基本一致纵势。
主線程只有一個踱阿,但是工作者線程可以有多個,并且每個工作線程與主線程都是相互獨立的钦铁,每個線程可以并行執(zhí)行腳本软舌。

注意:創(chuàng)建一個新的工作者線程的開銷比較大,所以不建議大量使用牛曹,通常一個工作者線程應(yīng)該是長期運行的佛点。

工作者線程主要分為:專用工作者線程、共享工作者線程和服務(wù)工作者線程躏仇。

三恋脚、執(zhí)行時機

基本名詞概念:

  • 同步任務(wù):在主線程上排隊執(zhí)行的任務(wù)腺办,按從上到下的順序執(zhí)行焰手。
  • 異步任務(wù):在任務(wù)隊列中等待執(zhí)行的任務(wù)糟描,通常是一個回調(diào)函數(shù)。當(dāng)任務(wù)隊列通知主線程某個異步任務(wù)可以執(zhí)行了书妻,這個任務(wù)就會進入主線程中執(zhí)行船响,任務(wù)隊列是FIFO(first in first out)順序執(zhí)行的。當(dāng)然躲履,定時器(setTimeout见间、setInterval)是一個例外。所以其實同步任務(wù)也可以看做直接執(zhí)行的異步任務(wù)工猜。
  • 宏任務(wù)和微任務(wù):異步任務(wù)又分為宏任務(wù)和微任務(wù)米诉,瀏覽器在執(zhí)行時,會先執(zhí)行宏任務(wù)篷帅,再執(zhí)行此宏任務(wù)產(chǎn)生的微任務(wù)史侣,再執(zhí)行下一個宏任務(wù),如此次宏任務(wù)沒有產(chǎn)生微任務(wù)魏身,則會直接執(zhí)行下一個宏任務(wù)惊橱。

常見宏任務(wù):
主線程代碼塊
setTimeout
setInterval
setImmediate ()-Node
requestAnimationFrame ()-瀏覽器

常見微任務(wù)
process.nextTick ()-Node
Promise.then()
catch
finally
Object.observe
MutationObserver

在node環(huán)境下,process.nextTick的優(yōu)先級高于Promise箭昵,可以簡單理解為在宏任務(wù)結(jié)束后會先執(zhí)行微任務(wù)隊列中的nextTickQueue部分税朴,然后才會執(zhí)行微任務(wù)中的Promise部分

事件循環(huán)(Event Loop):主線程先執(zhí)行完同步任務(wù),然后從任務(wù)隊列中讀取任務(wù)并執(zhí)行家制,一般來說正林,主線程執(zhí)行異步任務(wù)就是執(zhí)行回調(diào)函數(shù)。執(zhí)行完成之后又從任務(wù)隊列中讀取下一個任務(wù)颤殴,這一個重復(fù)讀取的過程叫做事件循環(huán)觅廓。

NodeJS的Event Loop
Node會先執(zhí)行所有類型為 timers 的 MacroTask,然后執(zhí)行所有的 MicroTask(NextTick例外)
進入 poll 階段诅病,執(zhí)行幾乎所有 MacroTask哪亿,然后執(zhí)行所有的 MicroTask
再執(zhí)行所有類型為 check 的 MacroTask,然后執(zhí)行所有的 MicroTask
再執(zhí)行所有類型為 close callbacks 的 MacroTask贤笆,然后執(zhí)行所有的 MicroTask
至此蝇棉,完成一個 Tick,回到 timers 階段

  • 定時觸發(fā)器(setTimeout和setInterval)是在單獨的定時觸發(fā)器線程中執(zhí)行計時的芥永,當(dāng)達到觸發(fā)條件之后篡殷,會將回調(diào)函數(shù)添加到異步隊列中等待執(zhí)行
  • http網(wǎng)絡(luò)請求是在XMLHttpRequest鏈接后新開一個線程進行請求,在請求狀態(tài)變化之后埋涧,相應(yīng)得把回調(diào)放進異步線程等待執(zhí)行

其他

setTimeout(fn, 0)

setTimeout(fn,0)的含義是板辽,指定某個任務(wù)在主線程最早可得的空閑時間執(zhí)行奇瘦,也就是說,盡可能早得執(zhí)行劲弦。它在"任務(wù)隊列"的尾部添加一個事件耳标,因此要等到同步任務(wù)和"任務(wù)隊列"現(xiàn)有的事件都處理完,才會得到執(zhí)行邑跪。
setTimeout()只是將事件插入了"任務(wù)隊列"次坡,必須等到當(dāng)前代碼(執(zhí)行棧)執(zhí)行完,主線程才會去執(zhí)行它指定的回調(diào)函數(shù)画畅。要是當(dāng)前代碼耗時很長砸琅,有可能要等很久,所以并沒有辦法保證轴踱,回調(diào)函數(shù)一定會在setTimeout()指定的時間執(zhí)行症脂,換句話說,setTimeout是主程序執(zhí)行完畢之后才開始計時

eg.
console.log(Date.parse(new Date()))
setTimeout(()=>{
  console.log(Date.parse(new Date()))
  console.log('setTimeout')
}, 0)
for (let i = 0; i<10000; i++){
  console.count('for')
}
console.log(Date.parse(new Date()))
console.log('endFor')

setImmediate()

與setTimeout(fn, 0)作用相似淫僻,但是執(zhí)行會在setTimeout(fn, 0)之后诱篷,下面兩個例子輸出一模一樣

setTimeout(()=>{
  console.log('setTimeout')
}, 0)
setImmediate(()=>{
    console.log('setImmediate')
})
console.log('hello world')
setImmediate(()=>{
    console.log('setImmediate')
})
setTimeout(()=>{
  console.log('setTimeout')
}, 0)
console.log('hello world')

輸出

hello world
setTimeout
setImmediate

經(jīng)典題目

請問以下代碼輸出是什么?

async function async1(){ 
  console.log('async1 start') 
  await async2() 
  console.log('async1 end') 
} 
async function async2(){ 
  console.log('async2') 
} 
console.log('script start') 
setTimeout(function(){ 
  console.log('setTimeout0')  
},0)   
setTimeout(function(){ 
  console.log('setTimeout3')
},3)   
setImmediate(() => console.log('setImmediate')); 
async1(); 
process.nextTick(() => console.log('nextTick')); 

new Promise(function(resolve){ 
  console.log('promise1') 
  resolve(); 
  console.log('promise2') 
}).then(function(){ 
  console.log('promise3') 
}) 
console.log('script end')

參考文章
阮一峰 JavaScript 運行機制詳解:再談Event Loop
帶你徹底弄懂Event Loop
硬核JS」一次搞懂JS運行機制
《JavaScript高級程序設(shè)計(第四版)》

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末嘁傀,一起剝皮案震驚了整個濱河市兴蒸,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌细办,老刑警劉巖橙凳,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異笑撞,居然都是意外死亡岛啸,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進店門茴肥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來坚踩,“玉大人,你說我怎么就攤上這事瓤狐∷仓” “怎么了?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵础锐,是天一觀的道長嗓节。 經(jīng)常有香客問我,道長皆警,這世上最難降的妖魔是什么拦宣? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上鸵隧,老公的妹妹穿的比我還像新娘绸罗。我一直安慰自己,他們只是感情好豆瘫,可當(dāng)我...
    茶點故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布珊蟀。 她就那樣靜靜地躺著,像睡著了一般靡羡。 火紅的嫁衣襯著肌膚如雪系洛。 梳的紋絲不亂的頭發(fā)上俊性,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天略步,我揣著相機與錄音,去河邊找鬼定页。 笑死趟薄,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的典徊。 我是一名探鬼主播杭煎,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼卒落!你這毒婦竟也來了羡铲?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤儡毕,失蹤者是張志新(化名)和其女友劉穎也切,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體腰湾,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡雷恃,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了费坊。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片倒槐。...
    茶點故事閱讀 39,703評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖附井,靈堂內(nèi)的尸體忽然破棺而出讨越,到底是詐尸還是另有隱情,我是刑警寧澤永毅,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布把跨,位于F島的核電站,受9級特大地震影響卷雕,放射性物質(zhì)發(fā)生泄漏节猿。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望滨嘱。 院中可真熱鬧峰鄙,春花似錦、人聲如沸太雨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽囊扳。三九已至吩翻,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間锥咸,已是汗流浹背狭瞎。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留搏予,地道東北人熊锭。 一個月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像雪侥,于是被迫代替她去往敵國和親碗殷。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,601評論 2 353

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