Node.js Event-loop事件循環(huán)

node.js事件循環(huán)


Node.js 是單進(jìn)程單線程應(yīng)用程序,但是通過(guò)事件和回調(diào)支持并發(fā),所以性能非常高。
Node.js 的每一個(gè) API 都是異步的,并作為一個(gè)獨(dú)立線程運(yùn)行,使用異步函數(shù)調(diào)用,并處理并發(fā)。
Node.js 基本上所有的事件機(jī)制都是用設(shè)計(jì)模式中觀察者模式實(shí)現(xiàn)。
Node.js 單線程類(lèi)似進(jìn)入一個(gè)while(true)的事件循環(huán),直到?jīng)]有事件觀察者退出,每個(gè)異步事件都生成一個(gè)事件觀察者,如果有事件發(fā)生就調(diào)用該回調(diào)函數(shù).

事件驅(qū)動(dòng)程序


想要理解Event Loop,就要從程序的運(yùn)行模式講起。運(yùn)行以后的程序叫做"進(jìn)程"(process),一般情況下,一個(gè)進(jìn)程一次只能執(zhí)行一個(gè)任務(wù)。
如果有很多任務(wù)需要執(zhí)行,不外乎三種解決方法。
(1)排隊(duì):因?yàn)橐粋€(gè)進(jìn)程一次只能執(zhí)行一個(gè)任務(wù),只好等前面的任務(wù)執(zhí)行完了,再執(zhí)行后面的任務(wù)。
(2)新建進(jìn)程:使用fork命令,為每個(gè)任務(wù)新建一個(gè)進(jìn)程。
(3)新建線程:因?yàn)檫M(jìn)程太耗費(fèi)資源,所以如今的程序往往允許一個(gè)進(jìn)程包含多個(gè)線程,由線程去完成任務(wù)。(進(jìn)程和線程的詳細(xì)解釋?zhuān)?qǐng)看這里。)

以JavaScript語(yǔ)言為例,它是一種單線程語(yǔ)言,所有任務(wù)都在一個(gè)線程上完成,即采用上面的第一種方法。一旦遇到大量任務(wù)或者遇到一個(gè)耗時(shí)的任務(wù),網(wǎng)頁(yè)就會(huì)出現(xiàn)"假死",因?yàn)镴avaScript停不下來(lái),也就無(wú)法響應(yīng)用戶(hù)的行為。
你也許會(huì)問(wèn),JavaScript為什么是單線程,難道不能實(shí)現(xiàn)為多線程嗎?
這跟歷史有關(guān)系。JavaScript從誕生起就是單線程。原因大概是不想讓瀏覽器變得太復(fù)雜,因?yàn)槎嗑€程需要共享資源、且有可能修改彼此的運(yùn)行結(jié)果,對(duì)于一種網(wǎng)頁(yè)腳本語(yǔ)言來(lái)說(shuō),這就太復(fù)雜了。后來(lái)就約定俗成,JavaScript為一種單線程語(yǔ)言。(Worker API可以實(shí)現(xiàn)多線程,但是JavaScript本身始終是單線程的。)
如果某個(gè)任務(wù)很耗時(shí),比如涉及很多I/O(輸入/輸出)操作,那么線程的運(yùn)行大概是下面的樣子。


單線程運(yùn)行任務(wù)示意圖

上圖的綠色部分是程序的運(yùn)行時(shí)間,紅色部分是等待時(shí)間。可以看到,由于I/O操作很慢,所以這個(gè)線程的大部分運(yùn)行時(shí)間都在空等I/O操作的返回結(jié)果。這種運(yùn)行方式稱(chēng)為"同步模式"(synchronous I/O)或"堵塞模式"(blocking I/O)。
如果采用多線程,同時(shí)運(yùn)行多個(gè)任務(wù),那很可能就是下面這樣。


多線程運(yùn)行任務(wù)示意圖

上圖表明,多線程不僅占用多倍的系統(tǒng)資源,也閑置多倍的資源,這顯然不合理。
Event Loop就是為了解決這個(gè)問(wèn)題而提出的。Wikipedia這樣定義:
Event Loop是一個(gè)程序結(jié)構(gòu),用于等待和發(fā)送消息和事件。(a programming construct that waits for and dispatches events or messages in a program.)

簡(jiǎn)單說(shuō),就是在程序中設(shè)置兩個(gè)線程:一個(gè)負(fù)責(zé)程序本身的運(yùn)行,稱(chēng)為"主線程";另一個(gè)負(fù)責(zé)主線程與其他進(jìn)程(主要是各種I/O操作)的通信,被稱(chēng)為"Event Loop線程"(可以譯為"消息線程")。

事件循環(huán)示意圖

上圖主線程的綠色部分,還是表示運(yùn)行時(shí)間,而橙色部分表示空閑時(shí)間。每當(dāng)遇到I/O的時(shí)候,主線程就讓Event Loop線程去通知相應(yīng)的I/O程序,然后接著往后運(yùn)行,所以不存在紅色的等待時(shí)間。等到I/O程序完成操作,Event Loop線程再把結(jié)果返回主線程。主線程就調(diào)用事先設(shè)定的回調(diào)函數(shù),完成整個(gè)任務(wù)。
可以看到,由于多出了橙色的空閑時(shí)間,所以主線程得以運(yùn)行更多的任務(wù),這就提高了效率。這種運(yùn)行方式稱(chēng)為"異步模式"(asynchronous I/O)或"非堵塞模式"(non-blocking mode)。
這正是JavaScript語(yǔ)言的運(yùn)行方式。單線程模型雖然對(duì)JavaScript構(gòu)成了很大的限制,但也因此使它具備了其他語(yǔ)言不具備的優(yōu)勢(shì)。如果部署得好,JavaScript程序是不會(huì)出現(xiàn)堵塞的,這就是為什么node.js平臺(tái)可以用很少的資源,應(yīng)付大流量訪問(wèn)的原因。

代碼體現(xiàn)


創(chuàng)建一個(gè)main.js文件

 //引入events模塊
 var events = require('events');
 //創(chuàng)建eventEmitter對(duì)象 內(nèi)部類(lèi)對(duì)象
 var eventEmitter = new events.EventEmitter();
 //創(chuàng)建事件處理程序
 var connectHandler = function connected() {
      console.log('連接成功');
     //觸發(fā)data_received事件
      evenEmitter.emit('data_received');
 }
 //創(chuàng)建 名為connection 事件,并將事件處理程序(回調(diào)函數(shù))綁定到事件上
 eventEmitter.on('connection',connectHandler);
 //創(chuàng)建名為data_reveived事件,并將匿名處理函數(shù)(回調(diào)函數(shù))綁定到事件上
 eventEmitter.on('data_received',function() {
       console.log('數(shù)據(jù)接收成功');
 })
 //觸發(fā)connection 事件
 eventEmitter.emit('connection');

 console.log("程序執(zhí)行完畢");

接下來(lái)讓我們執(zhí)行以上代碼:

$ node main.js
連接成功
數(shù)據(jù)接收成功
程序執(zhí)行完畢

看了上面的程序如果還不懂的話,請(qǐng)看下面的這幅圖,并結(jié)合最后一段話去理解:

node.js事件循環(huán)示意圖

事件相當(dāng)于一個(gè)主題(Subject),而所有注冊(cè)到這個(gè)事件上的處理函數(shù)相當(dāng)于觀察者(Observer)。
要點(diǎn):
1.觀察者相當(dāng)于事件處理程序--被回調(diào)的函數(shù)。
2.事件就相當(dāng)于我們認(rèn)為的任務(wù),比如程序執(zhí)行期間需要等待I/O這就是一個(gè)事件。
3.我們可以把node.js的主線程想像成一個(gè)來(lái)者不拒的主人,這個(gè)人遇到什么事情都不拒絕,但是他處理事情也要找其他人(I/O)幫忙。
他有一個(gè)管家叫event loop線程,當(dāng)事情來(lái)得時(shí)候,主人告訴管家這個(gè)事情,并讓管家去找能夠解決這個(gè)問(wèn)題的人,然后繼續(xù)接事情,當(dāng)能夠解決這個(gè)事情的人解決了事情,管家就把事情的結(jié)果告訴給主人,這里的回調(diào)函數(shù)可以想象成信鴿,主人將事情的結(jié)果放在信鴿身上(結(jié)果為參數(shù),信鴿為回調(diào)函數(shù)載體),完成整個(gè)任務(wù),這就說(shuō)明node.js是異步執(zhí)行的語(yǔ)言,非阻塞的語(yǔ)言,這也就是node.js性能高的原因。
4.node.js使用的是javascript語(yǔ)法,javascript語(yǔ)法是單線程的,當(dāng)用戶(hù)觸發(fā)事件,事件會(huì)產(chǎn)生消息,消息會(huì)進(jìn)入消息列表,在消息進(jìn)入列表的同時(shí),回調(diào)函數(shù)也進(jìn)入列表,當(dāng)消息出隊(duì)列時(shí),回調(diào)函數(shù)被調(diào)用。整個(gè)底層過(guò)程就是這樣的

吳海星譯


謝天謝地,實(shí)際情況不是這樣的。當(dāng)瀏覽器中有I/O操作時(shí),該操作會(huì)在事件輪詢(xún)的外面執(zhí)
行(腳本執(zhí)行的主順序之外),然后當(dāng)這個(gè)I/O操作完成時(shí),它會(huì)發(fā)出一個(gè)“事件”,①會(huì)有一個(gè)函
數(shù)(通常稱(chēng)作“回調(diào)”)處理它,如圖1-1所示。


Paste_Image.png

博客搬家:大坤的個(gè)人博客
歡迎評(píng)論哦~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,363評(píng)論 6 532
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,497評(píng)論 3 416
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 176,305評(píng)論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 62,962評(píng)論 1 311
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,727評(píng)論 6 410
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,193評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,257評(píng)論 3 441
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,411評(píng)論 0 288
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,945評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,777評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,978評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,519評(píng)論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,216評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,642評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,878評(píng)論 1 286
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,657評(píng)論 3 391
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,960評(píng)論 2 373

推薦閱讀更多精彩內(nèi)容

  • topics: 1.The Node.js philosophy 2.The reactor pattern 3....
    宮若石閱讀 1,102評(píng)論 0 1
  • 前言從Node.js進(jìn)入人們的視野時(shí),我們所知道的它就由這些關(guān)鍵字組成 事件驅(qū)動(dòng)、非阻塞I/O、高效、輕量,它在官...
    Www劉閱讀 1,554評(píng)論 0 18
  • 總結(jié)一: [node.js總結(jié)](http://www.cnblogs.com/Darren_code/archi...
    xiumeiii閱讀 1,913評(píng)論 0 14
  • 晚上刷朋友圈,突然發(fā)現(xiàn)J發(fā)了條動(dòng)態(tài):感謝大家的生日祝福【笑臉】 心想,完蛋了,我沒(méi)發(fā)祝福,關(guān)鍵是,我不記得他的生日...
    黃小云qh閱讀 1,229評(píng)論 0 0
  • 不知為何今天失眠了。趟在床上越是努力想讓自己睡著卻變得越來(lái)越清醒,很煎熬得趕腳。 失眠的時(shí)候最常想的是和你在一起...
    ws小三閱讀 221評(píng)論 0 0