探索node之事件循環(huán)的實(shí)現(xiàn)
事件循環(huán)
Node.js 是單進(jìn)程單線程應(yīng)用程序,但是因?yàn)?V8 引擎提供的異步執(zhí)行回調(diào)接口,通過(guò)這些接口可以處理大量的并發(fā),所以性能非常高。
Node.js 幾乎每一個(gè) API 都是支持回調(diào)函數(shù)的。
Node.js 基本上所有的事件機(jī)制都是用設(shè)計(jì)模式中觀察者模式實(shí)現(xiàn)。
Node.js 單線程類似進(jìn)入一個(gè)while(true)的事件循環(huán),直到?jīng)]有事件觀察者退出,每個(gè)異步事件都生成一個(gè)事件觀察者,如果有事件發(fā)生就調(diào)用該回調(diào)函數(shù).
進(jìn)程:CPU執(zhí)行任務(wù)的模塊
線程:模塊中的最小單元
舉個(gè)通俗的例子:
cpu比作我們每個(gè)人,到飯點(diǎn)吃飯了??梢渣c(diǎn)很多菜(cpu中的進(jìn)程):宮保雞丁,魚香肉絲,酸辣土豆絲。每樣菜具體包含了哪些內(nèi)容(cpu每個(gè)進(jìn)程中的線程):宮保雞丁(詳情:黃瓜、胡蘿卜、雞肉、花生米)。而詳情構(gòu)成了宮保雞丁這道菜,吃了以后不餓。就可以干活了,cpu中的進(jìn)程里的線程也是同理。當(dāng)線程完成自己的內(nèi)容將結(jié)果返回給進(jìn)程,進(jìn)程返回給cpu的時(shí)候。cpu就能處理日常需求。
- 單進(jìn)程單線程:一盤炒苦瓜,里面只有苦瓜。
- 單進(jìn)程多線程:一盤宮保雞丁,里面有黃瓜、胡蘿卜、雞肉、花生米
事件驅(qū)動(dòng)程序
Node.js使用事件驅(qū)動(dòng)模型,當(dāng)web server接收到請(qǐng)求,就把它關(guān)閉然后進(jìn)行處理,然后去服務(wù)下一個(gè)web請(qǐng)求
當(dāng)這個(gè)請(qǐng)求完成,它被放回處理隊(duì)列,當(dāng)?shù)竭_(dá)隊(duì)列開(kāi)頭,這個(gè)結(jié)果就返回給用戶。
這個(gè)模型非常高效可擴(kuò)展性非常強(qiáng),因?yàn)?webserver 一直接受請(qǐng)求而不等待任何讀寫操作。(這也稱之為非阻塞式IO或者事件驅(qū)動(dòng)IO)
在事件驅(qū)動(dòng)模型中,會(huì)生成一個(gè)主循環(huán)來(lái)監(jiān)聽(tīng)事件,當(dāng)檢測(cè)到事件時(shí)觸發(fā)回調(diào)函數(shù)。
整個(gè)事件驅(qū)動(dòng)的流程就是這么實(shí)現(xiàn)的,非常簡(jiǎn)潔。有點(diǎn)類似于觀察者模式,事件相當(dāng)于一個(gè)主題(Subject),而所有注冊(cè)到這個(gè)事件上的處理函數(shù)相當(dāng)于觀察者(Observer)。
Node.js 有多個(gè)內(nèi)置的事件,我們可以通過(guò)引入 events 模塊,并通過(guò)實(shí)例化 EventEmitter 類來(lái)綁定和監(jiān)聽(tīng)事件,如下實(shí)例:
// 引入events模塊 let events = require('events'); //創(chuàng)建eventEmitter對(duì)象 let eventEmitter = new events.EventEmitter(); //綁定事件以及事件處理程序 eventEmitter.on('eventName',eventHandler); //通過(guò)程序觸發(fā)事件 eventEmitter.emit('eventName')
示例:
//引入events模塊 let events = require('events'); //創(chuàng)建eventEmitter對(duì)象 let eventEmitter = new events.EventEmitter(); //創(chuàng)建事件處理程序 let connectHandle = function connected() { console.log('連接成功'); //觸發(fā)data_received事件 eventEmitter.emit('data_received') } //綁定connection事件處理程序 eventEmitter.on('connection', connectHandle); //使用匿名函數(shù)綁定data_received事件 eventEmitter.on('data_received', function () { console.log('數(shù)據(jù)接收成功'); }) //觸發(fā)connecttion事件 eventEmitter.emit('connection'); console.log('程序執(zhí)行完畢'); // 執(zhí)行結(jié)果: // 連接成功 // 數(shù)據(jù)接收成功 // 程序執(zhí)行完畢
eventEmitter.emit 是觸發(fā)事件(事件請(qǐng)求),eventEmitter.on是綁定處理事件的處理器(事件處理),事件的請(qǐng)求和處理是分開(kāi)的,所以是異步。
EventEmitter
node.js所有的異步I/O操作在完成時(shí)都會(huì)發(fā)送一個(gè)事件到事件隊(duì)列
node.js里面的許多對(duì)象都會(huì)分發(fā)事件:一個(gè)net.Server對(duì)象會(huì)在每次有新連接時(shí)觸發(fā)的一個(gè)事件,一個(gè)fs.readStream對(duì)象會(huì)在文件被打開(kāi)的時(shí)候觸發(fā)一個(gè)事件。所有這些產(chǎn)生事件的對(duì)象都是events.EventEmitter的實(shí)例
events 模塊只提供了一個(gè)對(duì)象: events.EventEmitter。EventEmitter 的核心就是事件觸發(fā)與事件監(jiān)聽(tīng)器功能的封裝。
EventEmitter 對(duì)象如果在實(shí)例化時(shí)發(fā)生錯(cuò)誤,會(huì)觸發(fā) error 事件。當(dāng)添加新的監(jiān)聽(tīng)器時(shí),newListener 事件會(huì)觸發(fā),當(dāng)監(jiān)聽(tīng)器被移除時(shí),removeListener 事件被觸發(fā)。
簡(jiǎn)單用法
var EventEmitter = require('events').EventEmitter; var event = new EventEmitter(); event.on('some_event', function() { console.log('some_event 事件觸發(fā)'); }); setTimeout(function() { event.emit('some_event'); }, 1000);
運(yùn)行這段代碼,1 秒后控制臺(tái)輸出了 ‘some_event 事件觸發(fā)'。
其原理是 event 對(duì)象注冊(cè)了事件 some_event 的一個(gè)監(jiān)聽(tīng)器,
然后我們通過(guò) setTimeout 在 1000 毫秒以后向 event 對(duì)象發(fā)送事件 some_event,此時(shí)會(huì)調(diào)用some_event 的監(jiān)聽(tīng)器。
EventEmitter 的每個(gè)事件由一個(gè)事件名和若干個(gè)參數(shù)組成,事件名是一個(gè)字符串,通常表達(dá)一定的語(yǔ)義。對(duì)于每個(gè)事件,EventEmitter 支持 若干個(gè)事件監(jiān)聽(tīng)器。
當(dāng)事件觸發(fā)時(shí),注冊(cè)到這個(gè)事件的事件監(jiān)聽(tīng)器被依次調(diào)用,事件參數(shù)作為回調(diào)函數(shù)參數(shù)傳遞。
var events = require('events'); var emitter = new events.EventEmitter(); emitter.on('someEvent', function(arg1, arg2) { console.log('listener1', arg1, arg2); }); emitter.on('someEvent', function(arg1, arg2) { console.log('listener2', arg1, arg2); }); emitter.emit('someEvent', 'arg1 參數(shù)', 'arg2 參數(shù)'); //輸出: // listener1 arg1 參數(shù) arg2 參數(shù) // listener2 arg1 參數(shù) arg2 參數(shù)
以上例子中,emitter 為事件 someEvent 注冊(cè)了兩個(gè)事件監(jiān)聽(tīng)器,然后觸發(fā)了 someEvent 事件。
運(yùn)行結(jié)果中可以看到兩個(gè)事件監(jiān)聽(tīng)器回調(diào)函數(shù)被先后調(diào)用。 這就是EventEmitter最簡(jiǎn)單的用法。
EventEmitter 提供了多個(gè)屬性,如 on 和 emit。on 函數(shù)用于綁定事件函數(shù),emit 屬性用于觸發(fā)一個(gè)事件。、
EventEmitter屬性
測(cè)試
/* * @Author: angula * @Date: 2020-09-21 22:29:18 * @LastEditTime: 2020-09-22 11:27:56 * @FilePath: \JS\Github-前端知識(shí)總結(jié)倉(cāng)庫(kù)\studySummary\Node.js學(xué)習(xí)筆記\事件循環(huán)\index2.js */ let events = require('events'); let eventEmitter = new events.EventEmitter(); // 監(jiān)聽(tīng)器1 let listener1 = function listener1() { console.log('監(jiān)聽(tīng)器listener1啟動(dòng)。。。'); } // 監(jiān)聽(tīng)器2 let listener2 = function listener2() { console.log('監(jiān)聽(tīng)器listener2啟動(dòng)。。。'); } // 綁定connection事件,處理函數(shù)為listener1 eventEmitter.addListener('connection', listener1); // 綁定connection事件,處理函數(shù)為listener2 eventEmitter.on('connection', listener2); //類,返回監(jiān)聽(tīng)器的數(shù)量 let eventListeners = eventEmitter.listenerCount('connection'); console.log(eventListeners + '個(gè)監(jiān)聽(tīng)器監(jiān)聽(tīng)連接事件。'); //處理connection事件 eventEmitter.emit('connection'); // 移除綁定的listener1 eventEmitter.removeListener('connection', listener1); console.log('listener1不再受監(jiān)聽(tīng)'); //觸發(fā)連接事件 eventEmitter.emit('connection'); eventListeners = eventEmitter.listenerCount('connection'); console.log(eventListeners + '個(gè)監(jiān)聽(tīng)器連接事件'); console.log('程序執(zhí)行完畢');
執(zhí)行結(jié)果:
到此這篇關(guān)于探索node之事件循環(huán)的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)node 事件循環(huán)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Node.js實(shí)現(xiàn)簡(jiǎn)單聊天服務(wù)器
Node.js 是一個(gè)基于Chrome JavaScript運(yùn)行時(shí)建立的一個(gè)平臺(tái), 用來(lái)方便地搭建快速的,易于擴(kuò)展的網(wǎng)絡(luò)應(yīng)用,今天我們來(lái)探討下,如何使用node.js實(shí)現(xiàn)簡(jiǎn)單的聊天服務(wù)器2014-06-06用Electron寫個(gè)帶界面的nodejs爬蟲的實(shí)現(xiàn)方法
這篇文章主要介紹了用Electron寫個(gè)帶界面的nodejs爬蟲的實(shí)現(xiàn)方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-01-01node.js中的fs.fsyncSync方法使用說(shuō)明
這篇文章主要介紹了node.js中的fs.fsyncSync方法使用說(shuō)明,本文介紹了fs.fsyncSync的方法說(shuō)明、語(yǔ)法、接收參數(shù)、使用實(shí)例和實(shí)現(xiàn)源碼,需要的朋友可以參考下2014-12-12Nodejs處理Json文件并將處理后的數(shù)據(jù)寫入新文件中
這篇文章主要介紹了Nodejs處理Json文件并將處理后的數(shù)據(jù)寫入新文件中,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-10-10Node.js實(shí)現(xiàn)的簡(jiǎn)易網(wǎng)頁(yè)抓取功能示例
這篇文章主要介紹了Node.js實(shí)現(xiàn)的簡(jiǎn)易網(wǎng)頁(yè)抓取功能示例,本文使用了PhantomJS、node-phantomjs等庫(kù)實(shí)現(xiàn),需要的朋友可以參考下2014-12-12Node.js readline 逐行讀取、寫入文件內(nèi)容的示例
本篇文章主要介紹了Node.js readline逐行讀取、寫入文件內(nèi)容的示例,運(yùn)用readline逐行讀取的兩種實(shí)現(xiàn),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-03-03Node.js刷新session過(guò)期時(shí)間的實(shí)現(xiàn)方法推薦
下面小編就為大家?guī)?lái)一篇Node.js刷新session過(guò)期時(shí)間的實(shí)現(xiàn)方法推薦。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-05-05