解剖事件環

解剖事件環
  1. js是單線程,是指主線程。但主線程以內還有不少非單線程的內容,好比定時器,好比promise,好比http請求。
  2. 在瀏覽器事件環機制中,當執行棧的同步代碼清空後,系統會去讀取任務隊列。什麼叫執行棧的同步代碼清空?其實就是主線程以內能一步過的代碼運行完畢。那麼什麼叫任務隊列?其實就是那些非單線程的內容,js執行的時候,會把這些異步的內容先保存起來,統一放到一個隊列裏,等待主線程裏的同步代碼運行完畢後再來處理這些任務,因此叫任務隊列。
  3. 所謂任務隊列,其實也是有分別的。這裏面分兩大類:宏任務和微任務。常見的宏任務包括setTimeout,setImmediate(只有ie支持),setInterval,messageChannel。常見的微任務則包括Promise.then(),mutationObserver。
  4. 既然任務隊列有分別,那麼處理這些任務的時候就有了優先級————優先處理微任務。把微任務清空後(若是處理過程當中有新的微任務,在處理當前微任務後會立刻處理新的微任務,並在下次執行宏任務前所有清空),再依次讀取宏任務,這裏特別注意,並不是一次性執行完全部宏任務,而是像隊列那樣,先取一個宏任務執行,執行完後,再去看是否有微任務,若是有,則執行微任務,而後再讀取一個宏任務執行,不斷循環。
  5. node.js中的事件環跟瀏覽器裏的事件環機制又有區別:在nodejs的事件環機制中,是優先執行微任務,可是當執行完微任務,進入宏任務的時候,即便在執行宏任務過程當中存在新的微任務,也不會優先執行微任務,而是把宏任務隊列中執行完畢。
  6. 例子:
setTimeout(() => {
  console.log('我是setTimeout1')
  Promise.resolve().then(()=>{
    console.log('我是Promise1')
  })
}, 0);

Promise.resolve().then(()=>{
  console.log('我是Promise2')
  setTimeout(() => {
    console.log('我是setTimeout2')
  }, 0);
  Promise.resolve().then(()=>{
    console.log('我是Promise3')
  })
  console.log('我是新插進來的')
})

console.log("我是最後一個")
複製代碼
打印順序爲:
'我是最後一個' -> '我是Promise2' -> '我是新插進來的' ->  '我是Promise3' -> '我是setTimeout1' -> '我是Promise1' -> '我是setTimeout2'
複製代碼
解析:
  1. js執行,碰到setTimeout,丟到任務隊列裏(宏任務),碰到promise,丟到任務隊列裏(微任務);先執行完主線程裏的單線程,也就是執行棧裏的同步代碼,輸出「我是最後一個」。
  2. 開始處理任務隊列。
  3. 優先處理微任務,打印'我是Promise2',碰到setTimeout,繼續丟到任務隊列,向下執行,輸出'我是Promise3'。
  4. 微任務處理完畢,去看看任務隊列裏有沒有宏任務。
  5. 處理先進去的那個setTimeout,輸出'我是setTimeout1',向下又發現了一個微任務Promise,丟到任務隊列。
  6. 好的,一個宏任務已經完成了,去看看有沒有微任務,發現了剛丟進去的promise,處理一下,輸出'我是Promise1'。
  7. 微任務全處理了,去看看有沒有微任務,發現了最後丟進去的那個碰到setTimeout,進行處理,輸出'我是setTimeout2'。至此所有輸出完畢。
相關文章
相關標籤/搜索