Event Loop模型理解

簡介

事件循環(event loop),是JavaScript用來解決因爲單線程阻塞形成執行效率低下的機制,也就是咱們常說的異步的基石。在不一樣的JavaScript宿主環境,Event Loop有着不一樣的模型。node

任務隊列

任務隊列是用來存放異步任務的回調,是一種先進先出的線性結構。因爲異步任務之間並不相同,任務隊列也由多個隊列組成。面試

宏任務隊列(microtask queue)

  • setTimeout/setTimeout
  • setImmediate
  • requestAnimationFrame
  • I/O
  • UI rendering (瀏覽器獨有)

微任務隊列(microtask queue)

  • process.nextTick (Node獨有)
  • Promise
  • Object.observe
  • MutationObserver

瀏覽器環境下的事件循環

事件循環模型

  1. 執行script同步代碼
  2. 執行棧爲空
  3. 從微任務隊列(microtask queue)中取出隊首的回調任務,放入調用棧中執行
  4. 繼續取出微任務隊列隊首的任務,放入調用棧中執行,以此類推,直到直到把微任務隊列中的全部任務都執行完畢
  5. 取出宏任務隊列(macrotask queue)中位於隊首的任務,放入調用棧中執行
  6. 重複2-5步驟

流程圖大體以下: chrome

瀏覽器中event loop流程圖

Nodejs中的事件循環

在node中,事件循環表現出的狀態與瀏覽器中大體相同。不一樣的是node中有一套本身的模型。node中事件循環的實現是依靠的libuv引擎。咱們知道node選擇chrome v8引擎做爲js解釋器,v8引擎將js代碼分析後去調用對應的node api,而這些api最後則由libuv引擎驅動,執行對應的任務,並把不一樣的事件放在不一樣的隊列中等待主線程執行。 所以實際上node中的事件循環存在於libuv引擎中。api

libuv的結構圖

事件循環模型

NodeJS的Event Loop中,執行宏隊列的回調任務有6個階段,以下圖:瀏覽器

nodejs中事件循環模型

各個階段執行的任務以下:異步

timer

timers 階段會執行 setTimeout 和 setInterval 回調,而且是由 poll 階段控制的。socket

I/O

執行除了close事件的callbacks、被timers設定的callbacks、setImmediate()設定的callbacks這些以外的callbacksoop

idle, prepare

僅node內部使用post

poll

poll 是一個相當重要的階段,這一階段中,系統會作兩件事情線程

  • 回到 timer 階段執行回調
  • 執行 I/O 回調

進入該階段時若是沒有設定了 timer:

  • 若是 poll 隊列不爲空,會遍歷回調隊列並同步執行,直到隊列爲空或者達到系統限制
  • 若是 poll 隊列爲空,且setImmediate回調須要執行,poll 階段會中止而且進入到 check 階段執行回調
  • 若是 poll 隊列爲空,且setImmediate 回調不須要執行,會等待回調被加入到隊列中並當即執行回調,這裏一樣會有個超時時間設置防止一直等待下去

若是設定了 timer:

  • poll 隊列爲空,則會判斷是否有 timer 超時,若是有的話會回到 timer 階段執行回調

check

check 階段執行 setImmediate的回調

close callbacks

執行socket.on('close', ....)這些callbacks

對於 microtask 來講,它會在以上每一個階段完成前清空microtask 隊列

參考文章

一次弄懂Event Loop(完全解決此類面試問題)

相關文章
相關標籤/搜索