詳解高階前端面試常問的EventLoop

嗯_嗯發表於2018-05-27

首先啥是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就記時,當時間到時就把此事件放到事件佇列中,遇到微任務就把微任務放到微任務空間,程式碼會繼續向下執行,直到同步程式碼執行完畢。

完畢後,會看看微任務空間中有沒有微任務,有就把微任務空間中的微任務全部執行,然後去佇列中取我們的事件執行,執行時若有微任務繼續放到微任務空間,當此事件執行完畢,還會把微任務空間中的微任務全部執行完畢,然後再去取佇列中的非同步任務。。。。反覆迴圈

有圖有真相(是不是很醜!醜也別說出來)

詳解高階前端面試常問的EventLoop

微任務和巨集任務

  • 微任務 Promise的then, (MutationObserver)
  • 巨集任務 setInterval, setTimeout, setImmediate(ie), MessageChannel

node端的EventLoop

想上個醜圖

詳解高階前端面試常問的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快
  
複製程式碼

相關文章