JavaScript執行機制-node事件循環

node環境下的事件循環機制

和瀏覽器有什麼不一樣?

在node中,事件循環表現出來的狀態和瀏覽器大體相同,可是node有一套本身的模型。javascript

node事件循環依靠libuv引擎,node選擇chrome v8 做爲js的解釋器,v8將js代碼分析後,去掉用node相關的api,這些api最後由libuv引擎驅動,執行對應任務,把不一樣事件放在不一樣隊列等待主線程執行。java

因此,實際上node中的事件循環存在於libuv引擎中。node

事件循環模型

各個階段詳解

node中大體的事件循環順序chrome

外部輸入數據---輪詢階段(poll)---檢查階段(check)---關閉事件回調階段(close callback)---定時器檢查階段(timer)---I/O事件回調階段(I/O callback)---閒置階段(idle,prepare)---輪詢階段...api

  • timers:執行定時器隊列回調 setTimeout() setInterval()
  • I/O callbacks:執行幾乎全部回調,不包括 close 事件,定時器,和setImmediate()回調
  • idle,prepare:僅在內部使用
  • poll:等待新的I/O事件,node在一些特殊狀況下會阻塞在這裏
  • check:setImmediate()回調在這個階段執行
  • close callbacks:如 socket.on('close',....)

poll 階段

當v8引擎將js代碼解析後傳入libuv引擎,首先計入poll階段。瀏覽器

poll階段執行邏輯:socket

先查看poll queue 是否有事件,有任務,按照先進先出依次執行回調。tcp

當queue爲空,會檢查是否有 setImmediate()的callback,若有,進入check階段的callback。同時也會檢查是否有到期的timer,若是有,就把到期的timer的callback按照調用順序,放入到 timer queue 中,以後循環進入 timer 階段的queue 中的callback。函數

這二者順序是不固定的,受運行環境影響。若是二者的queue都是空的,loop就在poll階段停留,直到有一個I/O事件返回。oop

poll階段執行poll queue中的回調實際上不會無限執行過下去。當 1 全部回調執行完畢 2 執行數超過了node限制,poll階段會終止執行 poll queue中的下一個回調。

check 階段

專門用來執行 setImmediate()方法的回調,當poll階段進入空閒,而且 setImmediate()裏面有callback,事件循環進入這個階段。

close 階段

當一個socket鏈接或者一個handle關閉(socket.destory())close事件會在這個階段執行回調。不然事件會用 process.nextTick()方法發送出去。

timer 階段

這個階段以先進先出的方式執行全部到期的timer加入到timer隊列裏面的callback,一個timer callback 指的是 經過 setTimeout或者 setInterval 函數設置的回調函數。

I/O callback 階段

這個階段主要執行大部分I/O事件的回調,包括一些操做系統執行的回調。

如:一個tcp鏈接出錯了,系統執行回調捕獲錯誤報告

process.nextTick(),setTimeout(),setImmediate()區別和使用場景

process.nextTick()

node中存在一種特殊的隊列,nextTick queue.

這個隊列回調執行雖然沒有被表示爲一個階段,可是這些時間會在每個階段完畢準備進入下一個階段時優先執行。 當事件循環進入下一個階段以前,會先檢查 nextTick queue是否有任務,若是有,會先清空這個隊列。不過須要注意這個操做在隊列清空前是不會中止的,因此,使用不當,會致使死循環,直至內存泄漏。

setTimeout(),setImmediate()

setTimeout()是定義一個回調,但願在必定時間後,第一時間去執行。but,受到各類影響,該回調並不會在時間間隔後,精準執行。node會在能夠執行timer回調的第一時間去執行你所設置的回調任務。

setImmediate(),字面上看,是當即執行。實際上,他會在一個固定的階段纔會執行回調,即poll階段以後。

誰會先執行????

setTimeout(() => {
    console.log('timeout');
}, 0);

setImmediate(() => {
    console.log('immediate');
});
複製代碼

答案是不必定,這取決於代碼運行環境。運行環境可能致使同步隊列裏面兩個方法順序隨機決定。

可是,在I/O事件的回調中,下面代碼順序是始終不會變的。

const fs = require('fs');

fs.readFile(__filename, () => {
    setTimeout(() => {
        console.log('timeout');
    }, 0);
    setImmediate(() => {
        console.log('immediate');
    });
});
答案永遠是不變的
immediate
timeout
複製代碼

由於在I/O事件的回調中,setImmediate方法的回調永遠在timer的回調前執行。

相關文章
相關標籤/搜索