在 node 11 版本中,node 下 Event Loop 已經與瀏覽器趨於相同。
在 node 11 版本中,node 下 Event Loop 已經與瀏覽器趨於相同。
在 node 11 版本中,node 下 Event Loop 已經與瀏覽器趨於相同。
Event Loop也是js老生常談的一個話題了。2月底看了阮一峯老師的《Node定時器詳解》一文後,發現沒法徹底對標以前看過的js事件循環執行機制,又查閱了一些其餘資料,記爲筆記,感受不妥,總結成文。html
瀏覽器中與node中事件循環與執行機制不一樣,不可混爲一談。
瀏覽器的Event loop是在HTML5中定義的規範,而node中則由libuv庫實現。同時閱讀《深刻淺出nodeJs》一書時發現比較當時node機制已有不一樣,因此本文node部分針對爲此文發佈時版本。強烈推薦讀下參考連接中的前三篇。html5
js執行爲單線程(不考慮web worker),全部代碼皆在執行線程調用棧完成執行。當執行線程任務清空後纔會去輪詢取任務隊列中任務。node
異步任務分爲task(宏任務,也可稱爲macroTask)和microtask(微任務)兩類。
當知足執行條件時,task和microtask會被放入各自的隊列中等待放入執行線程執行,咱們把這兩個隊列稱爲Task Queue(也叫Macrotask Queue)和Microtask Queue。git
即爲同步完成,一個宏任務,全部微任務,一個宏任務,全部微任務......github
new Promise((resolve, reject) =>{console.log(‘同步’);resolve()}).then(() => {console.log('異步')})
,即promise
的then
和catch
纔是microtask,自己的內部代碼不是。while (true) { 宏任務隊列.shift() 微任務隊列所有任務() }
js執行爲單線程,全部代碼皆在執行線程調用棧完成執行。當執行線程任務清空後纔會去輪詢取任務隊列中任務。web
在node中事件每一輪循環按照順序分爲6個階段,來自libuv的實現:api
除上述循環階段中的任務類型,咱們還剩下瀏覽器和node共有的microtask和node獨有的process.nextTick
,咱們稱之爲Microtask Queue和NextTick Queue。promise
咱們把循環中的幾個階段的執行隊列也分別稱爲Timers Queue、I/O Queue、Check Queue、Close Queue。瀏覽器
在進入第一次循環以前,會先進行以下操做:app
process.nextTick()
按照咱們的循環的6個階段依次執行,每次拿出當前階段中的所有任務執行,清空NextTick Queue,清空Microtask Queue。再執行下一階段,所有6個階段執行完畢後,進入下輪循環。即:
能夠看出,nextTick
優先級比promise
等microtask高。setTimeout
和setInterval
優先級比setImmediate
高。
setImmediate
則會在此輪循環的check階段執行,若是在timers階段建立了setTimeout
,因爲timers已取出完畢,則會進入下輪循環,check階段建立timers任務同理。setTimeout
優先級比setImmediate
高,可是因爲setTimeout(fn,0)
的真正延遲不可能徹底爲0秒,可能出現先建立的setTimeout(fn,0)
而比setImmediate
的回調後執行的狀況。while (true) { loop.forEach((階段) => { 階段所有任務() nextTick所有任務() microTask所有任務() }) loop = loop.next }
function sleep(time) { let startTime = new Date() while (new Date() - startTime < time) {} console.log('1s over') } setTimeout(() => { console.log('setTimeout - 1') setTimeout(() => { console.log('setTimeout - 1 - 1') sleep(1000) }) new Promise(resolve => resolve()).then(() => { console.log('setTimeout - 1 - then') new Promise(resolve => resolve()).then(() => { console.log('setTimeout - 1 - then - then') }) }) sleep(1000) }) setTimeout(() => { console.log('setTimeout - 2') setTimeout(() => { console.log('setTimeout - 2 - 1') sleep(1000) }) new Promise(resolve => resolve()).then(() => { console.log('setTimeout - 2 - then') new Promise(resolve => resolve()).then(() => { console.log('setTimeout - 2 - then - then') }) }) sleep(1000) })
瀏覽器輸出:
setTimeout - 1 //1爲單個task 1s over setTimeout - 1 - then setTimeout - 1 - then - then setTimeout - 2 //2爲單個task 1s over setTimeout - 2 - then setTimeout - 2 - then - then setTimeout - 1 - 1 1s over setTimeout - 2 - 1 1s over
node輸出:
setTimeout - 1 1s over setTimeout - 2 //一、2爲單階段task 1s over setTimeout - 1 - then setTimeout - 2 - then setTimeout - 1 - then - then setTimeout - 2 - then - then setTimeout - 1 - 1 1s over setTimeout - 2 - 1 1s over
由此也可看出事件循環在瀏覽器和node中的不一樣。
???