成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

圖解 Node.js 的核心 Event-loop

開發 前端
Node.js 是一個完完全全的消息驅動型模型。Node進程活著的最大意義是:有各種各樣的 Event 以及綁定在 Event 上面的Callback 和Data需要它(main thread 和 worker thread)處理。

這次我們來聊聊 Node.js 里面涉及到的一個核心概念:event-loop 。只有理解了它,才能明白 node 的進程模型,也才能明白異步調用在實現層面是什么樣子的,更能明白當同步代碼和異步代碼混雜在一起的時候,CPU 到底跑到我們代碼的哪一行了。文章分為兩篇:event-loop 篇和 Promise/Generator/async 篇。今天我們關注 event-loop 部分。

1、代碼思考

我寫了兩個函數,函數內部直接用 while(true){} 寫了一段死循環代碼。我們先來思考下面這段 Node.js code 執行結果是什么?很多人說 Node.js 是單線程的。如果是這樣,那 CPU 會不會陷入到 whileLoop_1() 的 while 循環里面出不來?

'use strict';
async function sleep(intervalInMS)
{
return new Promise((resolve,reject)=>{
setTimeout(resolve,intervalInMS);
});
}
async function whileLoop_1(){
while(true){
try {
console.log('new round of whileLoop_1');
await sleep(1000); // LINE-A
} catch (error) {
// ...
}
}
}
async function whileLoop_2(){
while(true){
try {
console.log('new round of whileLoop_2');
await sleep(1000); // LINE-B
} catch (error) {
// ...
}
}
}


whileLoop_1(); // LINE-C
whileLoop_2(); // LINE-D

不賣關子了,我先把執行結果發出來。

new round of whileLoop_1
new round of whileLoop_2
new round of whileLoop_1
new round of whileLoop_2
new round of whileLoop_1
new round of whileLoop_2
new round of whileLoop_1
new round of whileLoop_2
new round of whileLoop_1
new round of whileLoop_2
...

是的,正如你所見。這兩個 while 循環分別在交替執行, CPU 也沒有陷入到死循環里面出不來。那么問題來了:

  • CPU 執行到 LINE-A 的時候發生了什么使得它能成功脫身并有機會執行 whileLoop_2 ?
  • CPU 執行到 LINE-B 后,為什么又能回到 whileLoop_1 中繼續執行呢?

2、event-loop

在回答上面的問題前,我們需要先來看一個至關重要的概念:event-loop 。其實我們平時說 Node.js 是單線程僅僅是指 node 執行我們的 JS 代碼,更準確地說是 V8 執行 JS code 是發生在單線程里面的。實際上如果你打開 node 進程,會發現它有不少 worker thread。這是一個典型的單進程多線程模型。這些 worker thread 被放置于線程池里面,而 V8 執行 JS code 的線程被稱為主線程。主線程和線程池的配合關系如下圖所示。主線程負責執行 JS  code ,線程池里面的 worker thread 負責執行類似訪問 DB、訪問文件這樣的耗時費力的工作,它倆通過消息隊列協調工作。這和餐館工作流程類似。餐館由一個長得漂亮的小姐姐招呼客人落座并負責收集來自各個餐桌的點單。每當收到一個點好的菜單時,小姐姐會迅速地把它通過一個小窗口遞交給后廚。后廚那里有一個小看板,所有的點單都被陳列在看板上。廚師長根據訂單的時間和菜品安排不同的廚師燒菜。菜燒好后,再由小姐姐負責上菜。

圖片

圖 1:Node.js 單進程多線程模型

嗯上面這張圖還是太簡單了,用來騙新手可以,我知道滿足不了你們。我們把它放大一些。下圖中左邊是主線程,右邊是線程池和一個 worker thread,中間是消息隊列。

圖片

圖 2:Node.js 主線程和工作線程關系圖

(1)主線程

主線程只干一件事:拼命地執行 JS code,做 non-blocking I/O 操作。這些 JS code 既包含我們自己寫的,也包含我們所依賴的 npm package 。這里提到的 non-blocking I/O 操作意味著主線程干的事情基本上都是非阻塞型的工作,例如對 2+3 求和,迭代數組等。主線程以 tick 為粒度工作。是的,你一定聽說過 process.nextTick() ,所謂 next tick 就是下一次執行 tick 的時機。每個 tick 又包含若干個 phase ,按照 Node.js 官網介紹,目前為止一共有 6 個 Phase。

  • timers: 這個 phase 執行通過 setTimeout()? 和 setInterval() 所設置的 callback 函數。
  • pending callbacks: 這個 phase 執行一些與系統操作相關的 callback,比如建立 TCP 連接時收到的 ECONNREFUSED 相關的 callback 。
  • idle, prepare: 僅供Node.js內部使用。
  • poll: 從消息隊列里面獲取新的 I/O event,執行相應的 callback (不包括 setImmediate / close callback / 以及 timer 所設置的 callback)。
  • check: 執行通過 setImmediate()? 所設置的callback.
  • close callbacks: 執行一些 close callback ,比如通過這樣的代碼 socket.on('close', ...)? 所設置的 callback。

絕大部分情況下,這些 callback 是用 JS 寫的,Node 通過 Google V8 engine ,在主線程里面來執行這些 callback 。我們把上面的 6個 phase 和 tick 的關系放置到時間軸上,或許能更形象地說明主線程所做的工作。

圖片

圖 3:Node.js 主線程時序圖

(2)消息隊列

主線程不單單是在執行 JS code,也不僅僅只是在做 non-blocking I/O 操作。它在執行代碼的過程中,還會產生各種各樣的異步請求。直觀一點的如通過 setImmediate(callback[, ...args]) / fs.readFile(path[, options], callback) 產生,晦澀一點的如通過 Promise / async 產生。這些異步請求大部分情況下有一些共性:需要耗費一定的時間去處理。讓主線程放著其它事情不管,傻傻地干等這次操作的結果可不是聰明的做法。所以它們都會被封裝成 async Request,并被交給線程池去處理。還記得我們之前舉的餐館工作流程的例子嗎?燒菜是一個費時間的事情,如果小姐姐拿到我們的訂單,自己跑到后廚去燒菜會出現什么后果?等她把單子上的菜都燒好再去下一桌點菜的話,對客人而言就出現了一個 blocking I/O 操作:進餐館沒有人接待了。消息隊列就如同后廚那里的看板。小姐姐只負責往看板上添加新的訂單,而訂單的制作交由廚師團隊來完成。

(3)工作線程

工作線程來完成具體的 I/O 請求操作。通常這個過程藉由 OS 所提供的異步機制來完成。如 Windows 里面的 IO 完成端口(IOCP)、Linux 里面的異步 IO。如圖 2 所示,當工作線程完成了一個異步請求后,會把操作結果放置到一個消息隊列里面。從圖中可以看到,主線程運行所涉及到的每個 phase 都有各自專屬的消息隊列。消息隊列里面有了消息,意味著主線程又需要干活了,干活的過程中會繼續產生新的異步請求,工作線程繼續不知疲倦地搬磚。完美的閉環。有一種場景圖 2 并沒有畫出來,當 Node.js 收到來自系統外部的事件如網絡請求時,工作流程是什么樣子的?到目前為止我們談及的 event 都是由 JS code 主動觸發的,如果我們說這種 event 是由頂向下觸發的話,網絡請求這樣的 event 是由底向上觸發的。聰明的你一定可以在腦袋里大致畫出一條線出來:這條線的起點是位于內核的網卡驅動,終點是 Node.js 主線程,中間依次經過了內核協議棧,Node.js 的消息隊列。

3、小結

行文至此,可以看到 Node.js 是一個完完全全的消息驅動型模型。Node進程活著的最大意義是:有各種各樣的 event 以及綁定在 event 上面的 callback 和 data需要它(main thread 和 worker thread)處理。event 的 callback 中也可能會產生新的異步請求,進而產生新的 event 。正是這些源源不斷的 event 驅動著 Node 活下去。如果沒有event需要Node進程處理了,它也就沒有存在的必要了。Node.js 還是一個標準的單進行多線程模型。其中主線程用來執行我們所寫的 JS code ,而線程池里面的 worker thread 則用來執行各種耗時長的 I/O 操作。這些操作可能會導致 worker thread 被阻塞掉。worker thread 被阻塞沒有關系,但主線程被阻塞就不太美麗了。最后再強調一下:我們所寫的 JS code 是交由 V8 在單線程里面運行的,所以盡量不要在 JS code 里面執行耗時長的同步操作。

責任編輯:姜華 來源: 二哥聊云原生
相關推薦

2024-07-08 08:53:52

2014-03-07 13:43:32

Node.jsNode

2022-03-13 08:48:12

inspectorNode.js開發

2022-06-29 08:37:03

事件循環JS 語言

2013-11-01 09:34:56

Node.js技術

2015-03-10 10:59:18

Node.js開發指南基礎介紹

2020-05-29 15:33:28

Node.js框架JavaScript

2012-02-03 09:25:39

Node.js

2021-12-25 22:29:57

Node.js 微任務處理事件循環

2023-04-28 15:20:37

JavaScript事件循環

2017-10-09 18:54:20

前端Node.js貢獻代碼

2011-09-08 13:46:14

node.js

2011-11-01 10:30:36

Node.js

2011-09-02 14:47:48

Node

2011-09-09 14:23:13

Node.js

2022-02-12 20:33:29

Node.jsStreamfetch API

2011-11-10 08:55:00

Node.js

2012-10-24 14:56:30

IBMdw

2013-10-24 15:23:40

Event Loop

2021-09-26 05:06:04

Node.js模塊機制
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 天天干天天插 | 亚洲一区二区精品视频在线观看 | 99精品亚洲国产精品久久不卡 | 日韩欧美在线精品 | 特黄视频 | 亚洲 欧美 日韩 在线 | 精品影院 | 熟女毛片 | 国产91网站在线观看 | av香港经典三级级 在线 | 久久久久久亚洲精品 | 久久99国产精一区二区三区 | 久久久天堂 | 久久99精品久久久久久国产越南 | 一级在线观看 | 亚洲自拍偷拍视频 | av免费网站在线观看 | 黄网站免费观看 | 国产精品1区2区 | 亚洲国产一区二区三区四区 | 国产免费av在线 | 中文字幕国产高清 | 久久精品色欧美aⅴ一区二区 | 秋霞电影院午夜伦 | 久久1区 | 91网站在线看 | 日韩精品二区 | 亚洲国产欧美一区二区三区久久 | av在线播放一区二区 | 日韩成人av在线 | 呦呦在线视频 | 99精品在线观看 | 成人黄色三级毛片 | 国产精品久久久久久久久婷婷 | 一起操网站 | 欧美成人激情 | 国产一级在线视频 | 成人伊人 | 亚洲精品小视频在线观看 | 久久久久久综合 | 亚洲精品久久久久久宅男 |