我第一次看到他事件環(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的理解,但是還有很多雲裡霧裡的地方,寫出來的只是我理解的。