node環境和瀏覽器環境,表現出來的事件循環狀態,大致表現一致 惟一不一樣的是:javascript
- JS引擎存在 monitoring process 進程,會持續不斷的檢查主線程執行爲空,一旦爲空,就會去 callback queue 中檢查是否有等待被調用的函數。(只有宏任務和微任務兩個隊列)
- node 中是依靠 libuv 引擎實現,咱們書寫的 js 代碼有 V8 引擎分析後去調用對應的 nodeAPI ,這些 api 最後由 libuv 引擎驅動,在 libuv 引擎中有一套本身的模型,把不一樣的事件放在不一樣的隊列中等待主線程執行。( 模型中有6種宏任務隊列和1種微任務隊列 )
// libuv引擎中的事件模型,在每一個模型後面都添加了一些說明
┌───────────────────────────────────────────────────────┐
┌─>│ timers │ setTimeout/setInterval的回調
│ └──────────┬────────────────────────────────────────────┘
│ ↓
│ ┌──────────┴────────────────────────────────────────────┐
│ │ pending callbacks │ 處理網絡、流、tcp的錯誤回調
│ └──────────┬────────────────────────────────────────────┘
│ ↓
│ ┌──────────┴────────────────────────────────────────────┐
│ │ idle, prepare │ 只在node內部使用
│ └──────────┬────────────────────────────────────────────┘
│ ↓ ┌───────────────┐
│ ┌──────────┴────────────────────────────────────────────┐ │ incoming: │
│ │ poll │ 執行poll中的i/o隊列,檢查定時器是否到時 <------│ connections,
│ └──────────┬────────────────────────────────────────────┘ │ data, etc. │
│ ↓ └───────────────┘
│ ┌──────────┴────────────────────────────────────────────┐
│ │ check │ 存放setImmediate回調
│ └──────────┬────────────────────────────────────────────┘
│ ↓
│ ┌──────────┴────────────────────────────────────────────┐
└──┤ close callbacks │ 關閉的回調(socket.on('close')...)
└───────────────────────────────────────────────────────┘
複製代碼
官方的event-loop-timers-and-nexttick更詳細的說明java
setTimeout
和 setInterval
)setImmediate
回調這3個規定好的階段setImmediate()
的回調會在這個階段執行socket.on('close', ...)
process.nextTick
和 .then()
會在事件循環的階段切換過程當中執行(function test() {
setTimeout(function () { console.log(4) }, 0);
new Promise(function (resolve, reject) {
console.log(1);
for (var i = 0; i < 10000; i++) {
i == 9999 && resolve();
}
console.log(2);
}).then(function () {
console.log(5);
});
console.log(3);
})();
// 這段代碼是否是很熟悉
// 最終結果1,2,3,5,4 和 瀏覽器中效果一致
複製代碼
和上篇博客 從一道執行題,瞭解瀏覽器中JS執行機制 中的代碼同樣 (⊙﹏⊙)bnode
console.log(1)
setTimeout(() => {
console.log(2)
new Promise(resolve => {
console.log(4)
resolve()
}).then(() => {
console.log(5)
})
})
new Promise(resolve => {
console.log(7)
resolve()
}).then(() => {
console.log(8)
})
setTimeout(() => {
console.log(9)
new Promise(resolve => {
console.log(11)
resolve()
}).then(() => {
console.log(12)
})
})
// 瀏覽器中的結果:一、七、八、二、4 , 五、九、十一、12
// Node 中的結果:一、七、八、二、4 , 九、十一、五、12
複製代碼
解析以下:面試
- 在瀏覽器中
macro task
執行完成後,再次循環 宏任務 的回調隊列以前,會優先處理micro中的任務。所以結果是: 一、七、八、二、四、五、九、十一、12- 在
Node
中有6個宏任務隊列,事件循環首先進入 poll 階段。進入 poll 階段後查看是否有設定的 timers ( 定時器 )時間到達,若是有一個或多個時間到達, Event Loop 將會跳過正常的循環流程,直接從 timers 階段執行,並執行 timers 回調隊列,此時只有把 timers 階段的回調隊列執行完畢後。纔會走下一個階段,這也就是爲何setTimeout
中有.then
,而沒有被當即執行的緣由,當 timers 階段的回調隊列執行完畢後,切換到下一個階段這個過程當中去觸發 微任務(process.nextTick
和.then
) 。在階段與階段的切換之間。
setTimeout(function () {
console.log('setTimeout')
});
setImmediate(function () {
console.log('setImmediate')
});
複製代碼
執行結果:(
setTimeout、setImmediate
) 或 (setImmediate、setTimeout
)api
爲何?
setTimeout
在標準中默認的最小時間是4ms,若是開啓node和執行node代碼的時間小於4ms,那麼代碼解析完成後傳入libuv
引擎,首先會進入 poll 階段,此時查看設定的時間是否達到截止時間點,若是這個時間小於4ms( 沒有達到 ),那麼會走 check 階段,會觸發setImmediate
再觸發setTimeout
。若是開啓node和執行node代碼時間大於等於4ms,那麼就會先執行setTimeout
後執行setImmediate
瀏覽器
setImmediate(() => {
console.log('setImmediate1')
setTimeout(() => {
console.log('setTimeout1')
}, 0);
})
setTimeout(()=>{
process.nextTick(()=>console.log('nextTick'))
console.log('setTimeout2')
setImmediate(()=>{
console.log('setImmediate2')
})
},0);
複製代碼
兩種狀況 ( nextTick執行的位置:是在隊列切換時執行 )網絡
- 若是
setImmediate
先執行:setImmediate一、setTimeout二、setTimeout一、nextTick、setImmediate2
- 若是
setTimeout
先執行:setTimeout二、nextTick、setImmediate一、setImmediate二、setTimeout1
- Immediate當即執行的意思,其其實是固定在
check
階段纔會被執行。這個直譯的意義和process.nextTick
纔是最匹配的。- node的開發者們也清楚這兩個方法的命名上存在必定的混淆,他們表示不會把這兩個方法的名字調換過來---由於有大量的node程序使用着這兩個方法,調換命名所帶來的好處與它的影響相比不值一提。
- 可使咱們對異步代碼的執行順序有清晰的認知( 重要的 )
- 推遲任務執行
- 面試
這些概念遠比想象中的要重要異步
- 爲何
new Promise
第一個參數是同步執行的 ?學習Promise && 簡易實現Promise瀏覽器
中的 JS 執行機制是什麼樣子的?從一道執行題,瞭解瀏覽器中JS執行機制
附:這篇博客 也許 想表達 概念遠比想象中的要重要 (⊙﹏⊙)bsocket