在瀏覽器事件循環(huán)中,我們了解到javascript在瀏覽器中的事件循環(huán)機(jī)制,其是根據(jù)html5定義的規(guī)范來實(shí)現(xiàn)的
而在nodejs中,事件循環(huán)是基于libuv實(shí)現(xiàn),libuv是一個(gè)多平臺(tái)的專注于異步IO的庫,如下圖

image.png
上圖
EVENT_QUEUE給人看起來只有一個(gè)隊(duì)列,但是EventLoop存在6個(gè)階段,每個(gè)階段都有對(duì)應(yīng)的一個(gè)先進(jìn)先出的回調(diào)隊(duì)列
流程

image.png
- timers階段:這個(gè)階段執(zhí)行
timer(setTimeout,setInterval)的回調(diào) - 定時(shí)器檢測階段(timers):本階段執(zhí)行
timer的回調(diào),即setTimeout,setInterval里面的回調(diào)函數(shù) - I/O 事件回調(diào)階段:執(zhí)行延遲到下一個(gè)循環(huán)迭代的
I/O回調(diào),即上一輪循環(huán)中未被執(zhí)行的一些I/O回調(diào) - 限制階段:僅供系統(tǒng)內(nèi)部使用
- 輪訓(xùn)階段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í)候在此阻塞 - 檢查階段:
setImmediate()回調(diào)函數(shù)在這里執(zhí)行 - 關(guān)閉事件回調(diào)階段:一些關(guān)閉的回調(diào)函數(shù),如:
socket.on('close',……)
每個(gè)階段對(duì)應(yīng)一個(gè)隊(duì)列,當(dāng)事件循環(huán)進(jìn)入某個(gè)階段時(shí),將會(huì)在該階段內(nèi)執(zhí)行回調(diào),直到隊(duì)列好近或者回調(diào)的最大數(shù)量已執(zhí)行,那么將進(jìn)入下一個(gè)階段
除了上述6個(gè)階段,還存在process.nextTick,其部署事件循環(huán)的任何一個(gè)階段,屬于該階段與下階段之間的過度,即本階段執(zhí)行結(jié)束,進(jìn)入下一個(gè)階段前,所要執(zhí)行的回調(diào),類似插隊(duì)

image.png
在Nodejs中,同樣存在宏任務(wù)和微任務(wù),與瀏覽器中的時(shí)間循環(huán)相似
微任務(wù)對(duì)應(yīng)的有:
- next tick queue:process.nextTick
- other queue: Promise的then回調(diào),queueMicrotask
宏任務(wù)對(duì)應(yīng)的有:
- timer queue:setTimeout,setInterval
- poll queue: IO事件
- check queue: setImmediate
- close queue:close事件
其執(zhí)行順序?yàn)椋?/p>
- next tick microtask queue
- other microtask queue
- timer queue
- poll queue
- check queue
- close queue
async function async1() {
console.log('async1 start')
await async2()
console.log('async1 end')
}
async function async2(){
console.log("async2")
}
console.log("script start")
setTimeout(()=>{
console.log("setTimout0")
},0)
setTimeout(function () {
console.log('setTimeout2')
}, 300)
setImmediate(() => console.log('setImmediate'));
process.nextTick(() => console.log('nextTick1'));
async1();
process.nextTick(() => console.log('nextTick2'));
new Promise(function (resolve) {
console.log('promise1')
resolve();
console.log('promise2')
}).then(function () {
console.log('promise3')
})
console.log('script end')
分析過程:
- 先找到同步任務(wù),輸出script start
- 遇到第一個(gè)setTimout將里面的函數(shù)放入timer隊(duì)列中
- 遇到第二個(gè)setTimout,300ms后將里面的回調(diào)函數(shù)放到timer隊(duì)列中
- 遇到第一個(gè)setImmediate,將里面的回調(diào)函數(shù)放到check隊(duì)列中
- 遇到第一個(gè)nextTick,將里面的回調(diào)函數(shù)放到本輪同步任務(wù)執(zhí)行完畢后執(zhí)行
- 執(zhí)行async1 函數(shù),輸出async1 start
- 執(zhí)行async2 函數(shù),輸出async2 ,async2后面輸出async 1 end進(jìn)入微任務(wù),等待下一輪事件循環(huán)
- 遇到第二個(gè),將里面的回調(diào)函數(shù)放到本輪同步任務(wù)執(zhí)行完畢后執(zhí)行
- 遇到new Promise,執(zhí)行里面的立即執(zhí)行函數(shù),出書promise1,promise2
- then里面的回調(diào)函數(shù)進(jìn)入到微任務(wù)隊(duì)列
- 遇到同步任務(wù),輸出script end
- 執(zhí)行下一輪回調(diào)函數(shù),先依次輸出nextTick函數(shù),分別是nextTick1,nextTick2
- 然后執(zhí)行微任務(wù)隊(duì)列,依次輸出async1 end,promise3
- 執(zhí)行timer隊(duì)列,依次輸出setTimeout0
- 接著執(zhí)行check隊(duì)列,依次輸出setImmedate
- 300ms后,timer隊(duì)列存在任務(wù),執(zhí)行輸出setTimeout2
script start
async1 start
async2
promise1
promise2
script end
nextTick1
nextTick2
async1 end
promise3
setTimeout0
setImmediate
setTimeout2