Node.js 教程 05 - EventEmitter(事件監聽/發射器 )
目錄:html
前言前端
Node.js事件驅動介紹node
Node.js事件git
error事件服務器
前言:
今天事兒太多了,沒有發太多的東西。很差意思了各位。併發
本篇主要介紹Node.js中的事件驅動,至於Node.js事件概念的東西,太多了。
本系列課程主要抱着的理念就是,讓你們慢慢的入門,我也儘可能寫的簡單一點。
因此呢,本文事件驅動,你們的目標應該是:理解Node.js的事件驅動、會使用註冊事件及發射事件便可。
其餘的只做爲了解,在這裏就一筆而過了,若是你們想深刻了解,請自行百度。
Node.js事件驅動介紹:
Node.js在Github上有一句短短的介紹:Evented I/O for V8 Javascript。
一句話,霸氣側漏:基於V8引擎實現的事件驅動I/O,所以,Node.js也以事件驅動著名、經過異步的編程達到高吞吐量高性能。
Node.js能在衆多的後端Javascript技術中脫穎而出,正是因其事件的特色而受到歡迎。
拿Rhino來作比較,能夠看出Rhino引擎支持的後端JavaScript擺脫不掉其餘語言同步執行的影響,致使JavaScript在後端編程與前端編程之間有着十分顯著的差異,在編程模型上沒法造成統一。
在前端編程中,事件的應用十分普遍,DOM上的各類事件。在Ajax大規模應用以後,異步請求更獲得普遍的認同,而Ajax亦是基於事件機制的。
在Rhino中,文件讀取等操做,均是同步操做進行的。在這類單線程的編程模型下,若是採用同步機制,沒法與PHP之類的服務端腳本語言的成熟度媲美,性能也沒有值得可圈可點的部分。
直到Ryan Dahl在2009年推出Node.js後,後端JavaScript才走出其迷局。
Node.js的推出,我以爲該變了兩個情況:
- 統一了先後端JavaScript的編程模型。
- 利用事件機制充分利用用異步IO突破單線程編程模型的性能瓶頸,使得JavaScript在後端達到實用價值。
有了第二次瀏覽器大戰中的佼佼者V8的適時助力,使得Node.js在短短的兩年內達到可觀的運行效率,並迅速被你們接受。這一點從Node.js項目在Github上的流行度和NPM上的庫的數量可見一斑。
Node.js事件:
Node.js中,全部異步的I/O操做,在完成的時候都會發送一個事件到事件隊列中。
Node.js中的許多對象也都會分發事件,好比:
1. net.Server 對象會在每次有新連接時分發一個事件;
2. fs.readStream 對象會在文件被打開的時候分發一個事件;
3.。。。。。。
全部這些產生事件的對象都是 event.EventEmitter (事件監聽/發射器)的實例。咱們能夠經過「 require('events') 」來訪問該模塊。
Event模塊(event.EventEmitter)是一個簡單的事件監聽器模式的實現,具備 addListener 、 on 、 once 、 removelistener 、 removeAllListener 、 emit 等基本的事件監聽模式的方法實現。
它與前端DOM樹上的事件並不相同,由於它不存在事件冒泡,逐層捕獲等屬於DOM的事件行爲,也沒有preventDefault()、stopPropagation()、stopImmediatePropagation()等處理事件傳遞的方法。
從另外一個角度來看,事件偵聽器模式也是一種事件鉤子(hook)的機制,利用事件鉤子導出內部數據或狀態給外部調用者。
Node.js中的不少對象,大多具備黑盒的特色,功能點較少,若是不經過事件鉤子的形式,對象運行期間的中間值或內部狀態,是咱們沒法獲取到的。
這種經過事件鉤子的方式,可使編程者不用關注組件是如何啓動和執行的,只需關注在須要的事件點上便可。
回顧咱們以前建立HTTP服務器的Node.js代碼,小動更改,新建app.js代碼以下:
var http = require("http"); function onRequest(request, response){ console.log("Request received."); response.writeHead(200, {"Content-Type" : "text/plain"}); response.write("Hello World!"); response.end(); } http.createServer(onRequest).listen(88); console.log("Server has started!");
而後咱們運行「node app.js」,訪問瀏覽器便可看到效果:
而當咱們刷新頁面時,服務端便會建立一個請求:
【分析:(你們不要死扣這部份內容,只須要大概瞭解Node.js中事件的特色 - 「事件輪詢機制」就OK了。)】
這段代碼中,咱們使用函數onRequest封裝了請求的處理部分。當咱們啓動它會當即輸出「Server started!」。在咱們的瀏覽器訪問http://128.0.0.1:88時,會顯示消息「Request received。」
這兩個代碼的主要區別是,前者將處理部分寫在 http.createServer 中,按照傳統思路,啓動服務器後,遇到這段代碼會去運行,若是運行時間很長,致使暫停,很是沒有效率。若是第二位用戶請求的服務器,而它仍然在服務第一個請求,那第二個請求只能回答第一個完成後才能應答,這就是堵塞式Socket IO的問題。
Node.js的經過一個低級別的C / C + +層將異步執行有關IO的操做,一旦監聽到請求, Node.js將執行您做爲參數傳遞到I / O操做函數的回調函數,如上面的onRequest。這個異步操做關鍵是基於事件輪詢機制。
關於事件輪詢機制,內部覆蓋比較廣,我也就不增長本文篇幅了。若是感興趣,你們能夠自行百度,網上有案例講解。
註冊併發射自定義Node.js事件:
咱們如今要作的實例就是:使用Node.js註冊一個用戶自定義事件,而後再使用Node.js發射這個自定義事件。
步驟1:新建app.js,代碼以下:
1 var EventEmitter = require('events').EventEmitter; // 引入事件模塊 2 3 var event = new EventEmitter(); // 實例化事件模塊 4 5 // 註冊事件(customer_event) 6 event.on('customer_event', function() { 7 console.log('customer_event has be occured : ' + new Date()); 8 }); 9 10 setInterval(function() { 11 event.emit('customer_event'); // 發射(觸發)事件(customer_event) 12 }, 500);
上述代碼中,咱們首先使用require引入事件模塊的EventEmitter(註冊/發射器)。
而後,咱們實例化EventEmitter對象,存入到本地變量event中。
而後,咱們使用event對象的on函數,註冊一個名爲「customer_event」的自定義事件,事件的動做爲輸出一段信息。
最後,咱們使用setInterval函數,每500ms循環調用event對象的emit函數,來發射(觸發)咱們自定義的「customer_event」事件。
步驟2:運行「node app.js」,效果以下:
這個應該很好理解吧。
到此爲止,咱們知道了:使用EventEmitter對象的on註冊事件,而後使用對象的emit發射事件。
EventEmitter介紹:
events 模塊只提供了一個對象: events.EventEmitter。EventEmitter 的核心就是事件發射與事件監聽器功能的封裝。
咱們直接上例子吧。邊看邊說。
步驟1:新建app.js主程序,代碼以下:
1 var EventEmitter = require('events').EventEmitter; // 引入事件模塊 2 3 var event = new EventEmitter(); // 實例化事件模塊 4 5 // 註冊事件(sayHello) 6 event.on('sayHello', function(param1, param2) { 7 console.log('Hello1 : ', param1, param2); 8 }); 9 10 // 再次註冊事件(sayHello) 11 event.on('sayHello', function(param1, param2) { 12 console.log('Hello2 : ', param1, param2); 13 }); 14 15 event.emit('sayHello', 'GuYing', '1996'); // 發射(觸發)事件(sayHello)
上述代碼中,有兩點值得介紹的是事件參數,其次您可能以爲比較彆扭的是,這個事件註冊了兩次!
EventEmitter 的每個事件都是由一個事件名和若干個參數組成。事件名是一個字符串,一般表達必定的語義。對於每一個事件,EventEmitter 支持若干個事件監聽器。
當事件發射時,註冊到這個事件的事件監聽器被依次調用,事件參數做爲回調函數參數傳遞。
步驟2:運行「node app.js」,執行效果以下:
以上例子中,emitter 爲事件 someEvent 註冊了兩個事件監聽器,而後發射了 someEvent 事件。
運行結果中能夠看到兩個事件監聽器回調函數被前後調用。 這就是EventEmitter最簡單的用法。
EventEmitter經常使用的API:
EventEmitter.on(event, listener)、emitter.addListener(event, listener) 爲指定事件註冊一個監聽器,接受一個字 符串 event 和一個回調函數 listener。
server.on('connection', function (stream) { console.log('someone connected!'); });
EventEmitter.emit(event, [arg1], [arg2], [...]) 發射 event 事件,傳 遞若干可選參數到事件監聽器的參數表。
EventEmitter.once(event, listener) 爲指定事件註冊一個單次監聽器,即 監聽器最多隻會觸發一次,觸發後馬上解除該監聽器。
server.once('connection', function (stream) { console.log('Ah, we have our first user!'); });
EventEmitter.removeListener(event, listener) 移除指定事件的某個監聽 器,listener 必須是該事件已經註冊過的監聽器。
var callback = function(stream) { console.log('someone connected!'); }; server.on('connection', callback); // ... server.removeListener('connection', callback);
EventEmitter.removeAllListeners([event]) 移除全部事件的全部監聽器, 若是指定 event,則移除指定事件的全部監聽器。
error事件:
EventEmitter 定義了一個特殊的事件 error,它包含了"錯誤"的語義,咱們在遇到 異常的時候一般會發射 error 事件。
當 error 被髮射時,EventEmitter 規定若是沒有響 應的監聽器,Node.js 會把它看成異常,退出程序並打印調用棧。
咱們通常要爲會發射 error 事件的對象設置監聽器,避免遇到錯誤後整個程序崩潰。例如:
var events = require('events'); var emitter = new events.EventEmitter(); emitter.emit('error');
運行時會顯示如下錯誤:
node.js:201 throw e; // process.nextTick error, or 'error' event on first tick ^ Error: Uncaught, unspecified 'error' event. at EventEmitter.emit (events.js:50:15) at Object.<anonymous> (/home/byvoid/error.js:5:9) at Module._compile (module.js:441:26) at Object..js (module.js:459:10) at Module.load (module.js:348:31) at Function._load (module.js:308:12) at Array.0 (module.js:479:10) at EventEmitter._tickCallback (node.js:192:40)
繼承EventEmitter:
大多數時候咱們不會直接使用 EventEmitter,而是在對象中繼承它。包括 fs、net、 http 在內的,只要是支持事件響應的核心模塊都是 EventEmitter 的子類。
爲何要這樣作呢?緣由有兩點:
首先,具備某個實體功能的對象實現事件符合語義, 事件的監聽和發射應該是一個對象的方法。
其次JavaScript 的對象機制是基於原型的,支持 部分多重繼承,繼承 EventEmitter 不會打亂對象原有的繼承關係。
【來自:張董'Blogs:http://www.cnblogs.com/LonelyShadow,轉載請註明出處。】
親們。碼字不容易,以爲不錯的話記得點贊哦。。