nodejs運行機制

1.簡介

Node.js是一個事件驅(qū)動I/O服務(wù)端JavaScript環(huán)境,基于Google的V8引擎,V8引擎執(zhí)行Javascript的速度非常快,性能非常好。將libuv作為跨平臺抽象層,libuv是用c/c++寫成的高性能事件驅(qū)動的程序庫。nodejs的原理類似c/c++系統(tǒng)編程中的epoll

2.nodejs運行系統(tǒng)

?根據(jù)上圖,Node.js的運行機制如下。

(1)V8引擎解析JavaScript腳本。

(2)解析后的代碼,調(diào)用Node API。

(3)libuv庫負(fù)責(zé)Node API的執(zhí)行。它將不同的任務(wù)分配給不同的線程,形成一個Event Loop(事件循環(huán)),以異步的方式將任務(wù)的執(zhí)行結(jié)果返回給V8引擎。

(4)V8引擎再將結(jié)果返回給用戶。

注意:node.js的主進(jìn)程和Event Loop是分開的,主進(jìn)程執(zhí)行完同步代碼就不執(zhí)行了,剩下的就交給事件循環(huán)去做了。

3.事件循環(huán)(Event Loop)

nodejs 執(zhí)行之后會初始化一個事件循環(huán),執(zhí)行代碼程序(這些程序可能會造成異步調(diào)用、定時器或者process.nextTick()),然后開始執(zhí)行事件循環(huán)。

事件循環(huán)的執(zhí)行循序:


上邊的每一個模塊都是事件循環(huán)的一個階段,每個階段都有一個要執(zhí)行的回調(diào)的FIFO隊列。雖然每個階段都不同,一般來說,當(dāng)事件執(zhí)行到一個階段,先執(zhí)行這個階段特有的操作,然后操作這個階段的隊列,當(dāng)隊列執(zhí)行完或者達(dá)到了回調(diào)上限,事件循環(huán)就會執(zhí)行下一個階段。

各個階段介紹

timers:

執(zhí)行 setTimeout() 和 setInterval().的回調(diào)

var fs = require('fs');

function someAsyncOperation (callback) {

? // Assume this takes 95ms to complete

? fs.readFile('/Users/fyh13/fyh13/redis/nodes.conf', callback);

}

var timeoutScheduled = Date.now();

setTimeout(function () {

? var delay = Date.now() - timeoutScheduled;

? console.log(delay + "ms have passed since I was scheduled");

}, 100);

// do someAsyncOperation which takes 95 ms to complete

someAsyncOperation(function () {

? var startCallback = Date.now();

? // do something that will take 10ms...

? while (Date.now() - startCallback < 10) {

? ? ; // do nothing

? }

? console.log("someAsyncOperation end...")

});

執(zhí)行結(jié)果:

someAsyncOperation end...

102ms have passed since I was scheduled

從執(zhí)行結(jié)果來看,nodejs先執(zhí)行setTimeout,后執(zhí)行someAsyncOperation。執(zhí)行順序:進(jìn)來readFile隊列為空,setTimeout隊列也是空,readFile先執(zhí)行完成,將阻塞10ms的回調(diào)添加到poll隊列,執(zhí)行完后setTimeout還沒執(zhí)行完,等待,setTimeout時間到,執(zhí)行回調(diào)

I/O callbacks:

隊列執(zhí)行幾乎所有的異常行為的回調(diào)函數(shù),這些異常行為包括被定時器和setImmediate()預(yù)定的關(guān)閉的回調(diào)。 包括系統(tǒng)操作 例如TCP errors

idle, prepare:

內(nèi)部調(diào)用

poll:

檢索新的I/O事件;在適當(dāng)?shù)臅r候阻塞

1、執(zhí)行到這一步判斷poll隊列是否為空?

2、不為空執(zhí)行隊列,直到隊列為空或者回調(diào)數(shù)超過最大值?

3、為空 1)如果腳本被setImmediate()計劃好了,事件循環(huán)結(jié)束poll時期,繼續(xù)check時期去執(zhí)行計劃好的腳本;2)如果腳本沒有被setImmediate()計劃,事件循環(huán)等著回調(diào)被添加到隊列,然后立馬執(zhí)行

只要poll隊列為空,事件循環(huán)將檢查哪個定時器的時間已經(jīng)到了,如果有定時器時間到了,事件循環(huán)將回到timer時段處理這些定時的回調(diào)

小結(jié):poll隊列為空的時候,事件循環(huán)有可能執(zhí)行check階段執(zhí)行setImmediate的回調(diào),也有可能回到timer階段執(zhí)行setTimeout的回調(diào),這取決于setImmediate和setTimeout的回調(diào)哪個到得早。

check:

setImmediate() 的回調(diào)在這里執(zhí)行 當(dāng)程序執(zhí)行之后,一般情況下,事件循環(huán)會在poll階段等待連接請求,但是,如果代碼中有setImmediate()的回調(diào)函數(shù),并且poll階段空閑,事件循環(huán)就會繼續(xù)執(zhí)行check階段,而不會繼續(xù)在poll階段等在poll事件

close callbacks:

執(zhí)行像這樣的 socket.on('close', ...).

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容