欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

nodejs?快速入門之事件循環(huán)

 更新時間:2023年05月23日 08:21:49   作者:彭加李  
這篇文章主要介紹了nodejs?快速入門之事件循環(huán)的相關(guān)資料,需要的朋友可以參考下

瀏覽器中的事件循環(huán)

請?jiān)跒g覽器中運(yùn)行這段代碼:

console.log('1');
setTimeout(() => {
    console.log('2');
    Promise.resolve().then(() => console.log('3'));
    Promise.resolve().then(() => console.log('4'));
}, 100);
setTimeout(() => {
    console.log('5');
    Promise.resolve().then(() => console.log('6'));
}, 150);
Promise.resolve().then(() => console.log('7'));
setTimeout(() => console.log('8'), 200);
console.log('9');
/*
結(jié)果:
1
9
7
2
3
4
5
6
8
*/

分析這段代碼的事件循環(huán)的詳細(xì)過程之前,有幾點(diǎn)需要說一下:

  • 在一次事件循環(huán)中,只會執(zhí)行一個宏任務(wù)所有的微任務(wù),而且宏任務(wù)和微任務(wù)的處理順序是固定的:每次執(zhí)行完一個宏任務(wù)后,首先會立即處理所有的微任務(wù),然后才會執(zhí)行下一個宏任務(wù)。如果在執(zhí)行微任務(wù)時又產(chǎn)生了新的微任務(wù),那么這些新的微任務(wù)也會被添加到隊(duì)列中,直到全部微任務(wù)都執(zhí)行完成,才會執(zhí)行宏任務(wù)。
  • 宏任務(wù)執(zhí)行期間產(chǎn)生的微任務(wù)都會在當(dāng)前宏任務(wù)執(zhí)行完畢之后立即執(zhí)行,不會延遲到下一個宏任務(wù)或事件循環(huán)中執(zhí)行
  • 當(dāng)一個宏任務(wù)執(zhí)行的過程中產(chǎn)生了微任務(wù),那么這些微任務(wù)會被推入微任務(wù)隊(duì)列中等待處理。而只有當(dāng)當(dāng)前宏任務(wù)執(zhí)行結(jié)束之后,主線程才會去處理微任務(wù)隊(duì)列中的所有微任務(wù)。因此,所有的微任務(wù)都會在下一個宏任務(wù)執(zhí)行之前被處理完畢。
  • 在瀏覽器中,主線程使用輪詢方式來實(shí)現(xiàn)事件循環(huán)機(jī)制。在執(zhí)行完當(dāng)前的任務(wù)之后,如果宏任務(wù)隊(duì)列為空,主線程會等待一段時間,這個時間間隔是由瀏覽器廠商自行決定的,然后再次查詢宏任務(wù)隊(duì)列是否有任務(wù)需要執(zhí)行。
  • setTimeout 是宏任務(wù),比如執(zhí)行 setTimeout(() => console.log('8'), 200),瀏覽器會創(chuàng)建一個定時器(200ms),并將回調(diào)函數(shù)和指定的時間保存在一個任務(wù)中。當(dāng)指定的時間到達(dá)時,定時器才會將這個任務(wù)推入宏任務(wù)隊(duì)列中等待處理

這段代碼大概有四次事件循環(huán),執(zhí)行過程如下:

  • 第一次事件循環(huán):
首先將 console.log('1') 加入執(zhí)行棧中,輸出 1,然后將其從執(zhí)行棧中彈出。
第一個 setTimeout 函數(shù)被調(diào)用時,瀏覽器會創(chuàng)建一個定時器(100ms),并將回調(diào)函數(shù)和指定的時間保存在一個任務(wù)中。當(dāng)指定的時間到達(dá)時,定時器會將這個任務(wù)推入宏任務(wù)隊(duì)列中等待處理
第二個 setTimeout 與第一 setTimeout 類似,等待 150ms 后會被放入宏任務(wù)隊(duì)列中
Promise.resolve().then(() => console.log('7')) 放入微任務(wù)隊(duì)列
第三個 setTimeout 與第一 setTimeout 類似,等待 200ms 后會被放入宏任務(wù)隊(duì)列中
執(zhí)行 console.log('9')
取出微任務(wù)隊(duì)列中的所有任務(wù),輸出 7
  • 第二次事件循環(huán):
執(zhí)行棧為空,主線程輪詢查看宏任務(wù)隊(duì)列(微任務(wù)隊(duì)列剛才已經(jīng)清空了),此時宏任務(wù)隊(duì)列為空

100ms后,第一個setTimeout 宏任務(wù)推入宏任務(wù)隊(duì)列中,取出這個宏任務(wù)放入執(zhí)行棧中

輸出 2

執(zhí)行 `Promise.resolve().then(() => console.log('3'));`、`Promise.resolve().then(() => console.log('4'));`,放入微任務(wù)隊(duì)列

這個宏任務(wù)執(zhí)行完畢之后,主線程會轉(zhuǎn)而執(zhí)行當(dāng)前微任務(wù)隊(duì)列中的所有任務(wù),輸出 3 和 4
  • 第三次事件循環(huán):
執(zhí)行棧為空,主線程輪詢宏任務(wù)隊(duì)列發(fā)現(xiàn)其為空

150ms后,第二個setTimeout 宏任務(wù)推入宏任務(wù)隊(duì)列中,取出這個宏任務(wù)放入執(zhí)行棧中

輸出 5

執(zhí)行 `Promise.resolve().then(() => console.log('6'));` 放入微任務(wù)隊(duì)列

這個宏任務(wù)執(zhí)行完畢之后,主線程會轉(zhuǎn)而執(zhí)行當(dāng)前微任務(wù)隊(duì)列中的所有任務(wù),輸出 6
  • 第四次事件循環(huán):
執(zhí)行棧為空,主線程輪詢宏任務(wù)隊(duì)列發(fā)現(xiàn)其為空

200ms后,第三個setTimeout 宏任務(wù)推入宏任務(wù)隊(duì)列中,取出這個宏任務(wù)放入執(zhí)行棧中

輸出 8

宏任務(wù)優(yōu)先級

宏任務(wù)之間其實(shí)存在優(yōu)先級。比如 click > requestAnimationFrame > setTimeout

  • 用戶交互相關(guān)的任務(wù)具有最高的優(yōu)先級。在用戶交互(例如點(diǎn)擊)后,會將與該事件相關(guān)的任務(wù)添加到宏任務(wù)隊(duì)列中并標(biāo)記為緊急,從而使它們具有比其他任務(wù)更高的優(yōu)先級。這確保了與用戶直接交互相關(guān)的操作具有更快的響應(yīng)時間。
  • requestAnimationFrame函數(shù),這個函數(shù)也有較高的優(yōu)先級,因?yàn)樗枰谙乱淮纹聊凰⑿轮斑M(jìn)行處理以提供平滑的動畫效果
  • setTimeout 或 setInterval 添加的回調(diào)函數(shù)。通常情況下,先添加到隊(duì)列中的回調(diào)函數(shù)會優(yōu)先得到處理。它們只能保證至少在指定的時間后才開始執(zhí)行

請看示例:

function log(message) {
    const now = new Date();
    console.log(`[${now.getSeconds()}:${now.getMilliseconds()}] ${message}`);
}

setTimeout(() => {
    log('setTimeout callback');
}, 0);

requestAnimationFrame(() => {
    log('requestAnimationFrame callback');
});

document.addEventListener('click', () => {
    log('click event');
});

// 手動觸發(fā) click 事件
const event = new Event('click');
document.dispatchEvent(event);

/*
[46:280] click event
[46:299] setTimeout callback
[5:646] requestAnimationFrame callback
*/

無論測試多少次,click 總是最先輸出。但是 requestAnimationFrame 就不一定先 setTimeout 輸出,因?yàn)?requestAnimationFrame 有自己的節(jié)奏,只要不影響平滑的動畫效果,即使在 setTimeout 后面也可能。

核心特性

Node.js 核心的特性是事件驅(qū)動(Event-driven)和非阻塞 I/O(Non-blocking I/O):

  • 事件驅(qū)動 - nodejs 中的異步操作基于事件,也就是說,當(dāng)某個操作完成時,Node.js 會發(fā)出一個事件來通知你,然后你就可以通過注冊事件的方式來執(zhí)行回調(diào)函數(shù)。
  • 非阻塞 I/O - nodejs 執(zhí)行一個 I/O 操作時,它不會像傳統(tǒng)的同步阻塞 I/O 一樣等待操作完成,而是會在操作的同時繼續(xù)處理其他請求。這種方式可以避免 I/O 導(dǎo)致的阻塞,提高系統(tǒng)的吞吐量和響應(yīng)能力。

Tip:兩個特性有關(guān)系,但不是一個概念。比如可以說:基于事件驅(qū)動的非阻塞 I/O

Node.js 中的事件驅(qū)動和非阻塞 I/O 是基于事件循環(huán)實(shí)現(xiàn)的。

在 node 中,事件循環(huán)是一個持續(xù)不斷的循環(huán)過程,不斷地從事件隊(duì)列中取出事件并處理,直到事件隊(duì)列為空。具體來說,當(dāng) Node.js 遇到一個需要異步處理的 I/O 操作時,它不會等待操作完成后再執(zhí)行下一步操作,而是將該操作放到事件隊(duì)列中,并繼續(xù)執(zhí)行下一步。當(dāng)操作完成后,Node.js 會將相應(yīng)的回調(diào)函數(shù)也放到事件隊(duì)列中,等待事件循環(huán)來處理。這樣一來,Node.js 就可以同時處理多個請求,而且不會因?yàn)槟骋粋€操作的阻塞而影響整個應(yīng)用程序的性能。

除了 I/O 操作之外,事件循環(huán)還可以用于處理定時器HTTP 請求、數(shù)據(jù)庫訪問等各種類型的事件

Tip: 事件隊(duì)列不僅包含宏任務(wù)隊(duì)列微任務(wù)隊(duì)列,還有維護(hù)著幾個其他的隊(duì)列,這些隊(duì)列通過事件循環(huán)機(jī)制來實(shí)現(xiàn)異步非阻塞。其他隊(duì)列有:

  • check 隊(duì)列。check 隊(duì)列用于存放 setImmediate() 的回調(diào)函數(shù)
  • I/O 觀察器隊(duì)列(watcher queue)
  • 關(guān)閉事件隊(duì)列(close queue)

高并發(fā)和高性能

在 Node.js 中,高并發(fā)指的是系統(tǒng)能夠處理高并發(fā)請求的能力。不會因?yàn)橐粋€請求的處理而阻塞其他請求的執(zhí)行,系統(tǒng)能夠同時處理眾多請求。高性能通常指的是它在處理大量并發(fā)請求時表現(xiàn)出的優(yōu)異性能。

事件循環(huán)是 Node.js 實(shí)現(xiàn)高并發(fā)和高性能的核心機(jī)制之一。通過將計(jì)算密集型任務(wù)和 I/O 任務(wù)分離并采用異步執(zhí)行,Node.js 能夠充分利用 CPU 和內(nèi)存資源,從而實(shí)現(xiàn)高性能和高并發(fā)。

沒有事件循環(huán),Node.js 就無法實(shí)現(xiàn)異步 I/O 和非阻塞式編程模型。在傳統(tǒng)的阻塞式 I/O 模型中,一個 I/O 操作會一直等待數(shù)據(jù)返回,導(dǎo)致應(yīng)用程序被阻塞,無法進(jìn)行其他操作。而通過事件循環(huán)機(jī)制,Node.js 實(shí)現(xiàn)了異步 I/O,當(dāng)一個 I/O 操作被觸發(fā)后,Node.js 將其放入事件循環(huán)隊(duì)列中,然后立即執(zhí)行下一個任務(wù),不必等待當(dāng)前的 I/O 操作結(jié)束。當(dāng) I/O 操作完成時,Node.js 會將相應(yīng)的回調(diào)函數(shù)添加到事件隊(duì)列中等待執(zhí)行。

node 中的事件循環(huán)vs 瀏覽器中的事件循環(huán)

相同點(diǎn):單個主線程、單個執(zhí)行棧、有宏任務(wù)隊(duì)列和微任務(wù)隊(duì)列

不同點(diǎn):

  • 實(shí)現(xiàn)不同。Node.js 是一款服務(wù)端運(yùn)行時,而瀏覽器則用于頁面和交互等,場景不同,所以實(shí)現(xiàn)方式不同。Node.js 中的事件循環(huán)機(jī)制是通過 libuv 庫來實(shí)現(xiàn),因?yàn)樗哂锌缙脚_性、高效性、多功能性(除了事件循環(huán)機(jī)制外,libuv 還提供了很多其他的系統(tǒng)功能和服務(wù),能夠滿足 Node.js 在服務(wù)器端編程上的需要)等。
  • 一次事件循環(huán)不同。瀏覽器中的一次事件循環(huán)包括一個宏任務(wù)和相關(guān)所有微任務(wù)。在 node 中,一次事件循環(huán)包含6個階段(下文會詳細(xì)介紹)

雖然兩者有不同,但它們有相同的設(shè)計(jì)目標(biāo):高效而可靠的方式處理異步任務(wù)(或者說:解決 JavaScript 異步編程問題)。

原理

一次事件循環(huán)包含以下 6 個階段:

+--------------------------+
|                          |
|   timers                 | 計(jì)時器階段:處理 setTimeout() 和 setInterval() 定時器的回調(diào)函數(shù)。
|                          |
+--------------------------+
|                          |
|   pending callbacks      | 待定回調(diào)階段:用于處理系統(tǒng)級別的錯誤信息,例如 TCP 錯誤或者 DNS 解析異常。
|                          |
+--------------------------+
|                          |
|   idle, prepare          | 僅在內(nèi)部使用,可以忽略不計(jì)。
|                          |
+--------------------------+
|                          |
|   poll                   | 輪詢階段:等待 I/O 事件(如網(wǎng)絡(luò)請求或文件 I/O 等)的發(fā)生,然后執(zhí)行對應(yīng)的回調(diào)函數(shù),并且會處理定時器相關(guān)的回調(diào)函數(shù)。
|                          |          如果沒有任何 I/O 事件發(fā)生,此階段可能會使事件循環(huán)阻塞。
+--------------------------+
|                          |
|   check                  | 檢查階段:處理 setImmediate() 的回調(diào)函數(shù)。check 的回調(diào)優(yōu)先級比 setTimeout 高,比微任務(wù)要低
|                          |
+--------------------------+
|                          |
|   close callbacks        | 關(guān)閉回調(diào)階段:處理一些關(guān)閉的回調(diào)函數(shù),比如 socket.on('close')。
|                          |
+--------------------------+

這 6 個階段執(zhí)行順序:

  • 事件循環(huán)首先會進(jìn)入 timers 階段,執(zhí)行所有超時時間到達(dá)的定時器相關(guān)的回調(diào)函數(shù)。
  • 當(dāng) Node.js 執(zhí)行完 timers 階段后,就會進(jìn)入到 pending callbacks 階段。在這個階段, Node.js 會執(zhí)行一些系統(tǒng)級別的回調(diào)函數(shù),這些回調(diào)函數(shù)一般都是由 Node.js 的內(nèi)部模塊觸發(fā)的,而不是由 JavaScript 代碼直接觸發(fā)的。
  • 然后進(jìn)入 poll 階段,等待 I/O 事件的發(fā)生,處理相關(guān)的回調(diào)函數(shù)。如果在此階段確定沒有任何 I/O 事件需要處理,那么事件循環(huán)會等待一定的時間,以防止 CPU 空轉(zhuǎn),這個時間會由系統(tǒng)自動設(shè)置或者手動在代碼中指定。如果有定時器在此階段需要處理,那么事件循環(huán)會回到 timers 階段繼續(xù)執(zhí)行相應(yīng)的回調(diào)函數(shù)。
  • 接著進(jìn)入 check 階段,處理 setImmediate() 注冊的回調(diào)函數(shù)。setImmediate() 的優(yōu)先級比 timers 階段要高。當(dāng)事件循環(huán)進(jìn)入 check 階段時,如果發(fā)現(xiàn)事件隊(duì)列中存在 setImmediate() 的回調(diào)函數(shù),則會立即執(zhí)行該回調(diào)函數(shù)而不是繼續(xù)等待 timers 階段的到來。
  • 最后進(jìn)入 close callbacks 階段,處理一些關(guān)閉的回調(diào)函數(shù)。

事件循環(huán)的每個階段都有對應(yīng)的宏任務(wù)隊(duì)列微任務(wù)隊(duì)列。當(dāng)一個階段中的所有宏任務(wù)都執(zhí)行完之后,事件循環(huán)會進(jìn)入下一個階段。在該階段結(jié)束時,如果存在微任務(wù),事件循環(huán)將會在開始下一個階段之前執(zhí)行所有的微任務(wù)。這樣一來,無論在何時添加微任務(wù),都能確保先執(zhí)行所有的微任務(wù),避免了某些任務(wù)的并發(fā)問題。如果我們在某個階段中添加了多個微任務(wù),那么它們會在該階段結(jié)束時依次執(zhí)行,直到所有微任務(wù)都被處理完成,才會進(jìn)入下一個階段的宏任務(wù)隊(duì)列。

一次事件循環(huán)周期以清空6個階段的宏任務(wù)隊(duì)列和微任務(wù)隊(duì)列來結(jié)束。

一次事件循環(huán)周期內(nèi),每個階段是否可以執(zhí)行多次。例如此時在 poll 階段,這時 timers 階段任務(wù)隊(duì)列中有了回調(diào)函數(shù),由于 timers 的優(yōu)先級高于 poll,所以又回到 timers 階段,執(zhí)行完該階段的宏任務(wù)和微任務(wù)后,在回到 poll 階段。

總之,這 6 個階段構(gòu)成了 Node.js 的事件循環(huán)機(jī)制,確保了所有被注冊的回調(diào)函數(shù)都能得到及時、準(zhǔn)確的執(zhí)行

Tip:當(dāng)調(diào)用 setTimeout 方法時,如果超時時間還沒到,則生成的定時器宏任務(wù)也不會立刻放入宏任務(wù)隊(duì)列中,而是會被放入計(jì)時器隊(duì)列中。計(jì)時器隊(duì)列和延遲隊(duì)列類似,都是由定時器宏任務(wù)組成的小根堆結(jié)構(gòu),每個定時器宏任務(wù)也對應(yīng)著其到期時間以及對應(yīng)的回調(diào)函數(shù)。當(dāng)超時時間到達(dá)后,Node.js 會將該定時器宏任務(wù)從計(jì)時器隊(duì)列中取出并放入宏任務(wù)隊(duì)列中,等待事件循環(huán)去執(zhí)行。

盡管事件循環(huán)的機(jī)制比較明確,但由于各種因素的影響,具體的執(zhí)行順序仍然難以精確預(yù)測。其順序取決于當(dāng)前事件隊(duì)列中各個回調(diào)函數(shù)的執(zhí)行情況、耗時以及系統(tǒng)各種資源的利用情況等多種因素。每次事件循環(huán)的順序都不一定相同:

  • 例如,在事件循環(huán)的 poll 階段中,如果存在大量耗時較長的 I/O 回調(diào)函數(shù),則事件循環(huán)可能會在 poll 階段中花費(fèi)較長的時間。此時,即使定時器的超時時間到達(dá)了,事件循環(huán)也不會立即進(jìn)入 timers 階段,而是要先處理 poll 階段中還未完成的任務(wù)。

Tip: setTimeout 在node 中最小是1ms,在瀏覽器中是4ms。

示例

console.log("start");

setTimeout(() => {
  console.log("first timeout callback");
}, 1);

setImmediate(() => {
  console.log("immediate callback");
});

process.nextTick(() => {
  console.log("next tick callback");
});

console.log("end");

運(yùn)行10次node 輸出如下:

start
end
next tick callback
first timeout callback
immediate callback

執(zhí)行分析:

  • 先執(zhí)行同步代碼,輸出 start、end
  • setTimeout和setImmediate屬于宏任務(wù)
  • process.nextTick 是微任務(wù),輸出 next tick callback

現(xiàn)在的難點(diǎn)是 setImmediate 和 setTimeout 的回調(diào)哪個先執(zhí)行!

注:在某些特殊情況下,timers 階段和 check 階段的任務(wù)可能會交錯執(zhí)行。這通常發(fā)生在以下兩種情況下:

  • 當(dāng) timers 階段中存在長時間運(yùn)行的回調(diào)函數(shù)時(如一個耗時很長的 for 循環(huán)),會導(dǎo)致該階段阻塞,影響事件循環(huán)的正常執(zhí)行。在這種情況下,如果 check 階段中有一些較短的回調(diào)函數(shù)需要執(zhí)行,Node.js 可能會在 timers 階段中間中斷執(zhí)行,并立即進(jìn)入 check 階段處理已經(jīng)準(zhǔn)備好的回調(diào)函數(shù),然后再返回 timers 階段繼續(xù)執(zhí)行剩余的回調(diào)函數(shù)。
  • 當(dāng)注冊了 setImmediate() 和 setTimeout() 回調(diào)函數(shù)并且它們被分別安排到不同的事件循環(huán)周期中執(zhí)行時,這時候 setImmediate() 的回調(diào)函數(shù)可能會在 timers 階段的回調(diào)函數(shù)之前被執(zhí)行。這是因?yàn)?check 階段的任務(wù)隊(duì)列優(yōu)先級比 timers 階段的任務(wù)隊(duì)列要高,所以在下一個循環(huán)周期的 check 階段中,setImmediate() 的回調(diào)函數(shù)會被優(yōu)先處理。

根據(jù)結(jié)果,我們推測:setImmediate 和 setTimeout 都進(jìn)入了下一個循環(huán)周期,先執(zhí)行 timers 階段,在執(zhí)行 check 階段的回調(diào)。

Tip: 盡管 setImmediate 被稱為 "immediate",但它并不保證會立刻執(zhí)行。在 Node.js 的事件循環(huán)中,setImmediate() 的回調(diào)函數(shù)會被加入到 check 階段的任務(wù)隊(duì)列中,等到輪到 check 階段時才會執(zhí)行。

CPU 密集型場景

Node.js 不適合CPU 密集型場景。比如大量數(shù)學(xué)計(jì)算,可能會阻塞 Node.js 主線程。

比如一個 1 到 10億求和的請求:

const http = require('http');

http.createServer((req, res) => {
  console.log('start');
  let sum = 0;
  for (let i = 1; i <= 1000000000; i++) {
    sum += i;
  }
  console.log('end');

  res.writeHead(200, {'Content-Type': 'text/plain'});
  res.end(sum.toString());
}).listen(3000);

console.log('server running at http://localhost:3000/');

通過curl 檢測訪問 http://localhost:3000/ 的時間,分別是 1.754s1.072s、2.821s

Administrator@ MINGW64 /e/ (master)
$ time curl http://localhost:3000/
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    18    0    18    0     0     15      0 --:--:--  0:00:01 --:--:--    15500000000067109000

real    0m1.754s
user    0m0.000s
sys     0m0.078s

Administrator@ MINGW64 /e/ (master)
$ time curl http://localhost:3000/
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    18    0    18    0     0     20      0 --:--:-- --:--:-- --:--:--    21500000000067109000

real    0m1.072s
user    0m0.015s
sys     0m0.093s

Administrator@ MINGW64 /e/ (master)
$ time curl http://localhost:3000/
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    18    0    18    0     0      6      0 --:--:--  0:00:02 --:--:--     6500000000067109000

real    0m2.821s
user    0m0.031s
sys     0m0.077s

接著用node 內(nèi)置的 cluster 模塊將計(jì)算工作分配到4個子進(jìn)程中,訪問速度大幅度提升。

const http = require('http');
const cluster = require('cluster');

if (cluster.isMaster) {
  // 計(jì)算工作分配到4個子進(jìn)程中
  const numCPUs = require('os').cpus().length;
  const range = 1000000000;
  const rangePerCore = Math.ceil(range / numCPUs);
  let endIndex = 0;
  let sum = 0;

  for (let i = 0; i < numCPUs; i++) {
    const worker = cluster.fork();
    worker.on('message', function({ endIndex, result }) {
      sum += result;
      if (endIndex === range) {
        console.log(sum);
        // 啟動 Web 服務(wù)器,在主進(jìn)程中處理請求
        http.createServer((req, res) => {
          res.statusCode = 200;
          res.setHeader('Content-Type', 'text/plain');
          res.end(`The sum is ${sum}\n`);
        }).listen(3000, () => {
          console.log(`Server running at http://localhost:3000/`);
        });
      }
    });
    worker.send({ startIndex: endIndex + 1, endIndex: endIndex + rangePerCore });
    endIndex += rangePerCore;
  }
} else {
  process.on('message', function({ startIndex, endIndex }) {
    let sum = 0;
    for (let i = startIndex; i <= endIndex; i++) {
      sum += i;
    }
    process.send({ endIndex, result: sum });
  });
}

訪問時長分別是:0.230s、0.216s、0.205s

Administrator@ MINGW64 /e/ (master)
$ time curl http://localhost:3000/
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    30  100    30    0     0   2354      0 --:--:-- --:--:-- --:--:--  4285The sum is 500000000098792260


real    0m0.230s
user    0m0.000s
sys     0m0.109s

Administrator@ MINGW64 /e/ (master)
$ time curl http://localhost:3000/
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    30  100    30    0     0   2212      0 --:--:-- --:--:-- --:--:--  3750The sum is 500000000098792260


real    0m0.216s
user    0m0.000s
sys     0m0.078s

Administrator@ MINGW64 /e/ (master)
$ time curl http://localhost:3000/
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    30  100    30    0     0   2545      0 --:--:-- --:--:-- --:--:--  6000The sum is 500000000098792260


real    0m0.205s
user    0m0.000s
sys     0m0.078s

其他

pm2 的一個局限性

假如一個請求得花費(fèi)2秒(1 到 10億之和),使用 pm2 也不能減小請求時間。

pm2能做的是:比如一個 node 應(yīng)用單核(1個cpu內(nèi)核)可以支持一千個并發(fā)請求,現(xiàn)在并發(fā)四千個請求,由于超出能力,請求響應(yīng)會變慢?,F(xiàn)在通過 Pm2 在四核服務(wù)器中啟動4個node應(yīng)用,之前還存在負(fù)載均衡,這樣就可以支持四千個并發(fā)請求。

Tip:pm2的介紹請看這里

單線程

Node.js 是單線程的,這意味著所有事件循環(huán)(Event Loop)和 I/O 操作都在一個主線程中運(yùn)行。所以說,Node.js 中只存在一個事件循環(huán)和一個執(zhí)行上下文棧。

不過,Node.js 的實(shí)現(xiàn)并不簡單粗暴。它通過使用非阻塞 I/O、異步編程以及事件驅(qū)動機(jī)制,讓單線程可以支持高并發(fā)處理大量的 I/O 操作。Node.js 底層采用的是 libuv 庫來實(shí)現(xiàn)異步 I/O 模型,該庫在底層會使用 libev 和 libeio 等多種事件驅(qū)動框架來實(shí)現(xiàn)對底層 I/O 系統(tǒng)調(diào)用的封裝,從而讓單線程可以同時處理多個 I/O 任務(wù),避免了線程切換的開銷,提高了應(yīng)用程序的性能。

此外,在 Node.js 版本 10.5.0 之后,Node.js 引入了 worker_threads 模塊,支持通過創(chuàng)建子線程的方式來實(shí)現(xiàn)多線程。worker_threads 模塊提供了一套 API,使得開發(fā)者可以方便地創(chuàng)建和管理多個子線程,并利用多線程來加速處理計(jì)算密集型任務(wù)等場景。

總之,Node.js 是單線程的,但同時也通過采用異步 I/O 模型、事件驅(qū)動機(jī)制和多線程等技術(shù)手段,來支持高并發(fā)、高性能的應(yīng)用程序開發(fā)。

到此這篇關(guān)于nodejs 快速入門之事件循環(huán)的文章就介紹到這了,更多相關(guān)nodejs 事件循環(huán)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 把Node.js程序加入服務(wù)實(shí)現(xiàn)隨機(jī)啟動

    把Node.js程序加入服務(wù)實(shí)現(xiàn)隨機(jī)啟動

    這篇文章主要介紹了把Node.js程序加入服務(wù)實(shí)現(xiàn)隨機(jī)啟動,本文使用qckwinsvc實(shí)現(xiàn)這個需求,講解了qckwinsvc的安裝和使用,需要的朋友可以參考下
    2015-06-06
  • node.js學(xué)習(xí)之事件模塊Events的使用示例

    node.js學(xué)習(xí)之事件模塊Events的使用示例

    Nodejs中不存在瀏覽器中冒泡,捕獲這些行為,Nodejs中實(shí)現(xiàn)了events這個模塊,Nodejs中大多數(shù)模塊都集成了這個模塊,所以events是Nodejs中最重要的一個模塊。這篇文章主要給大家介紹了關(guān)于node.js學(xué)習(xí)教程之事件模塊Events的相關(guān)資料,需要的朋友可以參考下。
    2017-09-09
  • nodejs 使用nodejs-websocket模塊實(shí)現(xiàn)點(diǎn)對點(diǎn)實(shí)時通訊

    nodejs 使用nodejs-websocket模塊實(shí)現(xiàn)點(diǎn)對點(diǎn)實(shí)時通訊

    這篇文章主要介紹了nodejs 使用nodejs-websocket模塊實(shí)現(xiàn)點(diǎn)對點(diǎn)實(shí)時通訊的實(shí)例代碼,代碼簡單易懂,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下
    2018-11-11
  • node里的filesystem模塊文件讀寫操作詳解

    node里的filesystem模塊文件讀寫操作詳解

    這篇文章主要為大家介紹了node里的filesystem模塊文件讀寫操作詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-09-09
  • 基于游標(biāo)的分頁接口實(shí)現(xiàn)代碼示例

    基于游標(biāo)的分頁接口實(shí)現(xiàn)代碼示例

    這篇文章主要給大家介紹了關(guān)于基于游標(biāo)的分頁接口實(shí)現(xiàn)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-11-11
  • nodejs個人博客開發(fā)第一步 準(zhǔn)備工作

    nodejs個人博客開發(fā)第一步 準(zhǔn)備工作

    這篇文章主要為大家詳細(xì)介紹了nodejs個人博客開發(fā)的準(zhǔn)備工作,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-04-04
  • nvm安裝指定版本node失敗的解決方法

    nvm安裝指定版本node失敗的解決方法

    工作中,碰到一個項(xiàng)目需要舊版本的node運(yùn)行(版本為14.21.3),于是我用nvm isntall 14.21.3按照該版本Node, 出現(xiàn)了報(bào)錯,所以本文記錄一下nvm安裝指定版本node失敗的解決方法,需要的朋友可以參考下
    2025-04-04
  • Node.js、Socket.IO和GPT-4構(gòu)建AI聊天機(jī)器人的項(xiàng)目實(shí)踐

    Node.js、Socket.IO和GPT-4構(gòu)建AI聊天機(jī)器人的項(xiàng)目實(shí)踐

    本文主要介紹了Node.js、Socket.IO和GPT-4構(gòu)建AI聊天機(jī)器人的項(xiàng)目實(shí)踐,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-05-05
  • Node.js API詳解之 dgram模塊用法實(shí)例分析

    Node.js API詳解之 dgram模塊用法實(shí)例分析

    這篇文章主要介紹了Node.js API詳解之 dgram模塊用法,結(jié)合實(shí)例形式分析了Node.js API中dgram模塊基本功能、函數(shù)、使用方法及操作注意事項(xiàng),需要的朋友可以參考下
    2020-06-06
  • 淺析node Async異步處理模塊用例分析及常用方法介紹

    淺析node Async異步處理模塊用例分析及常用方法介紹

    這篇文章主要介紹了淺析node Async異步處理模塊用例分析及常用方法介紹,需要的朋友可以參考下
    2017-11-11

最新評論