關於Node中事件循環的解析

不言
發布: 2018-07-20 10:30:00
原創
1248 人瀏覽過

這篇文章要跟大家介紹的內容是關於Node中事件循環的解析,有著一定的參考價值,有需要的朋友可以參考一下。

事件循環中的各階段

Node.js 的事件循環流程大致如下:

┌───────────────────────────┐ ┌─>│ timers │ │ └─────────────┬─────────────┘ │ ┌─────────────┴─────────────┐ │ │ pending callbacks │ │ └─────────────┬─────────────┘ │ ┌─────────────┴─────────────┐ │ │ idle, prepare │ │ └─────────────┬─────────────┘ ┌───────────────┐ │ ┌─────────────┴─────────────┐ │ incoming: │ │ │ poll │<─────┤ connections, │ │ └─────────────┬─────────────┘ │ data, etc. │ │ ┌─────────────┴─────────────┐ └───────────────┘ │ │ check │ │ └─────────────┬─────────────┘ │ ┌─────────────┴─────────────┐ └──┤ close callbacks │ └───────────────────────────┘
登入後複製

每個階段都有自己的任務佇列,當本階段的任務隊列都執行完畢,或是達到了執行的最大任務數,就會進入到下一個階段。

timers 階段

這個階段會執行被setTimeoutsetInterval設定的定時任務。
當然,這個定時並不是準確的,而是在超過了定時時間後,一旦得到執行機會,就立刻執行。

pending callbacks 階段

這個階段會執行一些和底層系統有關的操作,例如TCP連線回傳的錯誤等。這些錯誤發生時,會被Node 延遲到下一個循環中執行。

輪詢階段

這個階段是用來執行和IO 操作有關的回調的,Node會向作業系統詢問是否有新的IO 事件已經觸發,然後會執行回應的事件回調。幾乎所有除了 定時器事件、setImmediate()close callbacks之外操作都會在這個階段執行。

check 階段

這個階段會執行setImmediate()設定的任務。

close callbacks 階段

如果一個sockethandle(句柄)突然被關閉了,例如透過socket.destroy()關閉了,close事件將會在這個階段發出。

事件循環的具體執行

事件循環初始化之後,會依照上圖所示的流程進行:

  1. ##首先會依序執行計時器中的任務、

    pending callback回呼;

  2. #然後進入到

    idleprepare階段,這裡會執行 Node內部的一些邏輯;

  3. 然後進入到

    poll輪詢階段。在這個階段會執行所有的 IO 回調,如 讀取文件,網路操作等。poll階段有一個poll queue任務佇列。這個階段的執行過程相對較長,如下所示:

  • 進入到本階段,會先檢查

    timeout定時佇列是否有可執行的任務,如果有,會跳到計時器階段執行。

  • 如果沒有

    計時器任務,就會檢查poll queue任務佇列,如果不為空,會遍歷執行所有任務直到都執行完畢或達到能執行的最大的任務數量。

  • poll queue任務佇列執行完成後,會檢查setImmediate任務佇列是否有任務,如果有的話,事件循環會轉移到下一個check階段。

  • 如果沒有

    setImmediate任務,那麼,Node 將會在此等待,等待新的 IO 回呼的到來,並立刻執行他們。

注意:這個等待不會一直等待下去,而是達到一個限定條件之後,繼續轉到下一個階段去執行。

setTimeout()setImmediate()

一個小秘訣

其實也不算是秘密,只是我是在剛剛查閱資料才知道的。

那就是:在 Node 中,
setTimeout(callback, 0)會轉換成setTimeout(callback, 1)詳情請參考 這裡 。

setTimeout()setImmediate()的執行順序

#下面這兩個定時任務執行的順序在不同情況下,表現不一致。

setTimeout(function() { console.log('timeout'); }, 0); setImmediate(function() { console.log('immediate'); });
登入後複製

普通程式碼中設定定時器

如果在普通的程式碼執行階段(例如在最外層程式碼區塊中),設定這兩個定時任務,他們的執行順序是不固定的。

  1. 首先,我們設定的

    setTimeout(callback, 0)已經轉換成setTimeout(callback, 1),所以進入定時器階段時,會根據目前時間判斷定時是否超過了1ms

  2. 事件循環在進入定時器階段之前會由系統呼叫方法來更新當前時間,由於系統中同時運行其他的程序,系統需要等待其他程序的進程運行結束才能取得準確時間,所以更新得到的時間可能會有一定的延遲。

  3. 更新時間時,若沒有延遲,定時不到

    1ms,immediate任務會先執行;如果有延遲,而這個時間達到了1ms的界限,timeout任務就會先執行。

在IO回呼中設定定時器

如果我們在IO 回呼中設定了這兩個定時器,那麼

setImmediate任務會先執行,原因如下:

  1. 进入poll phase轮询阶段之前会先检查是否有timer定时任务。

  2. 如果没有timer定时任务,才会执行后面的 IO 回调。

  3. 我们在 IO 回调中设置setTimeout定时任务,这时已经过了timer检查阶段,所以timer定时任务会被推迟到下一个循环中执行。

process.nextTick()

无论在事件循环的哪个阶段,只要使用process.nextTick()添加了回调任务,Node 都会在进入下一阶段之前把nextTickQueue队列中的任务执行完。

setTimeout(function() { setImmediate(() => { console.log('immediate'); }); process.nextTick(() => { console.log('nextTick'); }); }, 0); // nextTick // immediate
登入後複製

上述代码中,总是先执行nextTick任务,就是因为在循环在进入下一个阶段之前会先执行nextTickQueue中的任务。下面代码的执行结果也符合预期。

setImmediate(() => { setTimeout(() => { console.log('timeout'); }, 0); process.nextTick(() => { console.log('nextTick'); }); }); // nextTick // timeout
登入後複製

相关推荐:

关于Node异步 I/O的介绍

关于Node模块机制的解析

以上是關於Node中事件循環的解析的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
作者最新文章
最新問題
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!