我第一次看到他事件環(event-loop)的時候,我是一臉懵,這是什么鬼,是什么循環嗎,為什么event還要loop,不是都是一次性的嗎?
瀏覽器中和nodejs環境中的事件環是有一些區別的,這里我只研究了nodejs環境,小黑框情況下的事件環。
這里的事件環並不是指單獨一件事件的循環,而是我們寫的很多很多的事件按照一定地規則排着隊去執行,然后隊列清空后繼續排隊,就是事件環。
事件環很復雜,這里我只有能力解釋事件環中的幾個點:
- node.js中對於事件環的解釋
- 宏任務(macro-task),微任務(micro-task)
node.js中對於事件環的解釋
nodejs中將eventloop分成了:
- timers: 定時器setTimeout執行,將callback加入隊列中。
- pending callbacks: 一些I/O的callback,推遲到下一次循環中執行。
- idle, prepare: 內部的一些事件。
- poll: 定時器的callback執行,setImmediate執行,微任務執行。
- check: setImmediate的callback執行。
- close callbacks: 一些callbacks的關閉,如socket。
這邊我們專注於timers、poll和check這三個階段。其他的我們用的不多。
timers、poll、check階段
- timers
這個階段,只執行setTimeout和setInterval,但是他們的callback不會執行,而是推到宏任務的隊列之中。
- poll
這個階段,會先執行符合條件的微任務,比如Promise的異步完成,如果是setImmediate,則只會執行,不執行他的callback,然后執行定時器的callback,比如timeout。這里會適當得暫停一會,看看會不會有新任務進入隊列。如果有setImmediate的callback則進入check 階段,否則回到timer繼續新一輪循環。
- check
當poll階段的隊列完成,則會輪到check,這時會執行setImmediate的callback。如果沒有需要關閉callbacks,那么就回到timer繼續新一輪的循環。
宏任務 vs 微任務
- 宏任務
從我的角度理解,就是一個正常的task,本來在一個線程中可以毫無波折地一個接着一個運行到最后,奈何每個宏任務執行之后都有可能產生一些微任務,因此很不幸,這些宏任務就要排在這些微任務之后了。
宏任務代表:script(整體代碼),setTimeout,setImmediate。
/**
output:
我先走一步
你太慢了,我插個隊
老司機,等等我
*/
setTimeout(()=>{
console.log("我先走一步")
})
setTimeout(()=>{
console.log("老司機,等等我")
},10)
setImmediate(()=>{
console.log("你太慢了,我插個隊")
})
划重點
setTimeout和setImmediate,觸發的階段不同,因此callback執行時間也不同。但是如果setTimeout的時間過長,那么系統會先執行setImmediate,然后等下一輪詢中,如果setTimeout到時間了,那么就運行setTimeout的callbacks。
- 微任務
就是宏任務執行時,產生的新的小任務,比如異步,此類任務稱之為微任務,一般在當前宏任務執行完之后“插隊”執行。
微任務代表:process.nextTick, Promise(原生)。
划重點
雖然process.nextTick和Promise都是微任務,但是他們的執行的先后順序是不一樣的。無論誰的代碼先執行,等到了poll階段,兩者都是可運行的狀態時,都是nextTick先於Promise執行。
/**
output:
本宮始終是你望成莫及的
總有一日,我會上位
*/
Promise.resolve().then(()=>{
console.log("總有一日,我會上位")
})
process.nextTick(()=>{
console.log("本宮始終是你望成莫及的")
})
后記:
我只寫了我對於eventloop的理解,但是還有很多雲里霧里的地方,寫出來的只是我理解的。