首先啥是EventLoop?
整個js這種執行機制又稱為Event Loop(事件迴圈)
EventLoop又分為瀏覽器端和node端兩種
上程式碼感受一下
setTimeout(() => {
console.log(1)
Promise.resolve(3).then(data => console.log(data))
}, 0)
setTimeout(() => {
console.log(2)
}, 0)
//瀏覽器 1 3 2
//node 1 2 3
複製程式碼
瀏覽器的EventLoop
首先就是執行時會有一個棧 然後有一個事件佇列和微任務空間(自己命名的)
當我們的程式碼從上到下同步執行時,遇到setTimeout就記時,當時間到時就把此事件放到事件佇列中,遇到微任務就把微任務放到微任務空間,程式碼會繼續向下執行,直到同步程式碼執行完畢。
完畢後,會看看微任務空間中有沒有微任務,有就把微任務空間中的微任務全部執行,然後去佇列中取我們的事件執行,執行時若有微任務繼續放到微任務空間,當此事件執行完畢,還會把微任務空間中的微任務全部執行完畢,然後再去取佇列中的非同步任務。。。。反覆迴圈
有圖有真相(是不是很醜!醜也別說出來)
微任務和巨集任務
- 微任務 Promise的then, (MutationObserver)
- 巨集任務 setInterval, setTimeout, setImmediate(ie), MessageChannel
node端的EventLoop
想上個醜圖
再來段文字描述 爭取把自己繞暈
- setTimeout 和 setImmediate 執行順序不固定 取決於node的準備時間
setImmediate 設計在poll階段完成時執行,即check階段; setTimeout 設計在poll階段為空閒時,且設定時間到達後執行;但其在timer階段執行 其二者的呼叫順序取決於當前event loop的上下文,如果他們在非同步i/o callback之外呼叫(在i/o內呼叫因為下一階段為check階段),其執行先後順序是不確定的,需要看loop的執行前的耗時情況
- 上圖每一個方框都對應一個事件佇列,當event loop執行到某個階段時會將當前階段對應的佇列依次執行。當佇列執行完畢或者執行的數量超過上線時,會轉入下一個階段
- 微任務是在切換對列是執行
廢話不多說,上程式碼
setImmediate(() => {
console.log('setImmediate1')
setTimeout(() => {
console.log('setTimeout1')
}, 0);
})
setTimeout(()=>{
console.log('setTimeout2')
setImmediate(()=>{
console.log('setImmediate2')
})
},0);
//首先setImmediate和setTimeout執行順序不固定
//所以setImmediate1和setTimeout2,列印順序不定
//假設 setImmediate1先列印 有兩種順序
//1.執行順序是 setImmediate1 setTimeout2 setTimeout1 setImmediate2
//2.執行順序是 setImmediate1 setTimeout2 setImmediate2 setTimeout1
// 1.setImmediate1列印後把setTimeout1放進timer佇列 然後執行setTimeout2 這時候把setImmediate2放進check佇列中 列印setTimeout2 發現timer中還有setTimeout1 他就會把timer對列中的執行完 才會執行下一佇列的程式碼
//然後列印setTimeout1, 切換對列到check 列印setImmediate2
//2. setImmediate1列印後把setTimeout1放進timer佇列 然後執行setTimeout2 這時候把setImmediate2放進check佇列中 列印setTimeout2 這個時候node執行過快setTimeout1還沒有到時間,所以切換對列執行setImmediate2 然後是setTimeout1
//setTimeout第二個引數雖然是0,但是都有預設時間一般是4ms左右
複製程式碼
再來一題
setImmediate(() => {
console.log('setImmediate1')
setTimeout(() => {
console.log('setTimeout1')
}, 0);
})
setTimeout(()=>{
console.log('setTimeout2')
process.nextTick(()=>console.log('nextTick'))
setImmediate(()=>{
console.log('setImmediate2')
})
},0);
//假設setImmediate1先
//setImmediate1 setTimeout2 setTimeout1 nextTick setImmediate2
//setImmediate1 setTimeout2 nextTick setImmediate2 setTimeout1
//微任務會在切換對列時執行 上面從timer對列切換到check對列時nextTick執行
//上面一題明白了這一個也明白了
複製程式碼
再來一題
let fs = require('fs');
fs.readFile('./1.txt',function () {
console.log(1);
setTimeout(() => {
console.log('setTimeout')
}, 0);
setImmediate(() => {
console.log('setImmediate')
});
});
//順序固定 1 setImmediate setTimeout
// 為什麼呢?看上面的圖 首先readFile肯定是poll對列 執行時把setTimeout setImmediate放到各自佇列 然後按上下順序查詢對列 查詢到check佇列發現有 就把setImmediate列印出 然後順序檢查下一佇列close沒有 迴圈 再次從上往下找 直到timer列印setTimeout
複製程式碼
以上內容是本人試出來的 mac vscode
微任務有兩種 nextTick和then 那麼這兩個誰快呢?
Promise.resolve('123').then(res=>{
console.log(res);
})
process.nextTick(() => console.log('nextTick'))
//順序 nextTick 123
//很明顯 nextTick快
複製程式碼