什么是Event Loop?
js是單線程語言,代碼需要一句一句往下執(zhí)行粘秆,會(huì)出現(xiàn)阻塞粹胯。Event Loop是解決這一問題的機(jī)制,讓js實(shí)現(xiàn)異步蒋困,在不同的js執(zhí)行環(huán)境中(瀏覽器盾似、node)表現(xiàn)不同。
瀏覽器中的Event Loop
js自上而下執(zhí)行代碼
同步任務(wù)雪标,在調(diào)用棧中按照順序等待主線程依次執(zhí)行
異步任務(wù)零院,在EventTable中注冊回調(diào)事件,等異步任務(wù)有結(jié)果后村刨,將注冊的回調(diào)函數(shù)放入對應(yīng)的任務(wù)隊(duì)列中
任務(wù)隊(duì)列有宏任務(wù)隊(duì)列和微任務(wù)隊(duì)列告抄。
一個(gè)線程僅有一個(gè)微任務(wù)隊(duì)列,微任務(wù)包括:promise.then/catch/final嵌牺、Mutation Observer打洼、process.nextTick(node)龄糊。除此之外的事件,均為宏任務(wù)募疮。
一個(gè)線程可以有多個(gè)宏任務(wù)隊(duì)列分別處理不同類型的事件炫惩,如鼠標(biāo)鍵盤事件可放在一個(gè)宏任務(wù)隊(duì)列中,優(yōu)先執(zhí)行保證用戶體驗(yàn)阿浓,其他類型事件可分別放在不同的宏任務(wù)隊(duì)列中他嚷。
執(zhí)行順序:一個(gè)宏任務(wù) -> 全部微任務(wù) -> 一個(gè)宏任務(wù) -> 全部微任務(wù) -> ...直至兩個(gè)隊(duì)列都為空。同步任務(wù)相當(dāng)于一個(gè)宏任務(wù)芭毙,在最開始執(zhí)行筋蓖。
promise.xxx屬于es6范疇,規(guī)范中對于其是否視為微任務(wù)并不明晰退敦,所以各瀏覽器實(shí)現(xiàn)上有所不同粘咖,影響執(zhí)行順序。Google將其視為微任務(wù)侈百,Safari和Firefox將其視為宏任務(wù)瓮下。
async await是promise的語法糖,可轉(zhuǎn)為promise.then來看待设哗。
node中的Event Loop
node的Event Loop一共分為6個(gè)階段唱捣,每個(gè)細(xì)節(jié)具體如下:
- timers:本階段執(zhí)行已經(jīng)被 setTimeout() 和 setInterval() 的調(diào)度回調(diào)函數(shù)。
- pending callbacks:執(zhí)行延遲到下一個(gè)循環(huán)迭代的 I/O 回調(diào)网梢。
- idle, prepare:僅系統(tǒng)內(nèi)部使用震缭。
- poll:檢索新的 I/O 事件;執(zhí)行與 I/O 相關(guān)的回調(diào)(幾乎所有情況下,除了關(guān)閉的回調(diào)函數(shù)战虏,那些由計(jì)時(shí)器和 setImmediate() 調(diào)度的之外)拣宰,其余情況 node 將在適當(dāng)?shù)臅r(shí)候在此阻塞。
- check:setImmediate() 回調(diào)函數(shù)在這里執(zhí)行烦感。
- close callbacks:一些關(guān)閉的回調(diào)函數(shù)巡社,如:socket.on('close', ...)。
┌───────────────────────────┐
┌─>│ timers │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ pending callbacks │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ idle, prepare │
│ └─────────────┬─────────────┘ ┌───────────────┐
│ ┌─────────────┴─────────────┐ │ incoming: │
│ │ poll │<─────┤ connections, │
│ └─────────────┬─────────────┘ │ data, etc. │
│ ┌─────────────┴─────────────┐ └───────────────┘
│ │ check │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
└──┤ close callbacks │
└───────────────────────────┘
process.nextTick事件單獨(dú)有一個(gè)隊(duì)列存放手趣,當(dāng)每一階段執(zhí)行完畢后晌该,先執(zhí)行process.nextTick queue里的全部,再執(zhí)行micro queue里的全部绿渣,然后再進(jìn)入下一階段朝群。
為了和瀏覽器更加趨同,node v11版本將timer階段的setTimeout,setInterval...和在check階段的immediate改為一旦執(zhí)行一個(gè)階段里的一個(gè)任務(wù)就立刻執(zhí)行微任務(wù)隊(duì)列中符。node v10版本還是保持自己的執(zhí)行方式姜胖。
對比總結(jié)
瀏覽器環(huán)境下,microtask 的任務(wù)隊(duì)列是每個(gè) macrotask 執(zhí)行完之后執(zhí)行淀散。而在 Node.js 中右莱,microtask 會(huì)在事件循環(huán)的各個(gè)階段之間執(zhí)行蚜锨,也就是一個(gè)階段執(zhí)行完畢,就會(huì)去執(zhí)行 microtask 隊(duì)列的任務(wù)慢蜓。