咱們常常會聽到引擎和runtime,它們的區別是什麼呢?javascript
require
來引入模塊;而在瀏覽器中,咱們有 window
、 DOM。Js引擎是單線程的,如上圖中,它負責維護任務隊列,並經過 Event Loop 的機制,按順序把任務放入棧中執行。而圖中的異步處理模塊,就是 runtime 提供的,擁有和Js引擎互不干擾的線程。接下來,咱們會細說圖中的:棧和任務隊列。html
如今,咱們要運行下面這段代碼:java
function bar() { console.log(1); } function foo() { console.log(2); far(); } setTimeout(() => { console.log(3) }); foo();
它在棧中的入棧、出棧過程,以下圖:
node
Js 中,有兩類任務隊列:宏任務隊列(macro tasks)和微任務隊列(micro tasks)。宏任務隊列能夠有多個,微任務隊列只有一個。那麼什麼任務,會分到哪一個隊列呢?git
瀏覽器的 Event Loop 遵循的是 HTML5 標準,而 NodeJs 的 Event Loop 遵循的是 libuv。 區別較大,分開講。github
咱們上面講到,當stack空的時候,就會從任務隊列中,取任務來執行。瀏覽器這邊,共分3步:web
Event Loop 會無限循環執行上面3步,這就是Event Loop的主要控制邏輯。其中,第3步(更新UI渲染)會根據瀏覽器的邏輯,決定要不要立刻執行更新。畢竟更新UI成本大,因此,通常都會比較長的時間間隔,執行一次更新。vim
從執行步驟來看,咱們發現微任務,受到了特殊待遇!咱們代碼開始執行都是從script(全局任務)開始,因此,一旦咱們的全局任務(屬於宏任務)執行完,就立刻執行完整個微任務隊列。看個例子:segmentfault
console.log('script start'); // 微任務 Promise.resolve().then(() => { console.log('p 1'); }); // 宏任務 setTimeout(() => { console.log('setTimeout'); }, 0); var s = new Date(); while(new Date() - s < 50); // 阻塞50ms // 微任務 Promise.resolve().then(() => { console.log('p 2'); }); console.log('script ent'); /*** output ***/ // one macro task script start script ent // all micro tasks p 1 p 2 // one macro task again setTimeout
上面之因此加50ms的阻塞,是由於 setTimeout
的 delayTime 最少是 4ms. 爲了不認爲 setTimeout
是由於4ms的延遲然後面才被執行的,咱們加了50ms阻塞。api
NodeJs 的運行是這樣的:
NodeJs 的 Event Loop 分6個階段執行:
┌───────────────────────────┐ ┌─>│ timers │ │ └─────────────┬─────────────┘ │ ┌─────────────┴─────────────┐ │ │ pending callbacks │ │ └─────────────┬─────────────┘ │ ┌─────────────┴─────────────┐ │ │ idle, prepare │ │ └─────────────┬─────────────┘ ┌───────────────┐ │ ┌─────────────┴─────────────┐ │ incoming: │ │ │ poll │<─────┤ connections, │ │ └─────────────┬─────────────┘ │ data, etc. │ │ ┌─────────────┴─────────────┐ └───────────────┘ │ │ check │ │ └─────────────┬─────────────┘ │ ┌─────────────┴─────────────┐ └──┤ close callbacks │ └───────────────────────────┘
以上的6個階段,具體處理的任務以下:
setTimeout()
和setInterval()
設定的回調。setImmediate()
設定的回調。socket.on('close', ...)
的回調。每一個階段執行完畢後,都會執行全部微任務(先 nextTick,後其它),而後再進入下一個階段。