node.js 事件發射器模式

Node.js 教程 05 - EventEmitter(事件監聽/發射器 )

 

目錄:html

前言前端

Node.js事件驅動介紹node

Node.js事件git

註冊併發射自定義Node.js事件編程

EventEmitter介紹後端

EventEmitter經常使用的API瀏覽器

error事件服務器

繼承EventEmitter微信

 


 

 

前言:

  今天事兒太多了,沒有發太多的東西。很差意思了各位。併發

 

  本篇主要介紹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的推出,我以爲該變了兩個情況:

  1. 統一了先後端JavaScript的編程模型。
  2. 利用事件機制充分利用用異步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,轉載請註明出處。】

親們。碼字不容易,以爲不錯的話記得點贊哦。。

分類: Node.js
4
0
« 上一篇: Express 教程 01 - 入門教程之經典的Hello World
» 下一篇: Impress.js上手 - 拋開PPT、製做Web 3D幻燈片放映
posted @ 2015-03-04 18:22 張董 閱讀( 2123) 評論( 9) 編輯 收藏
相關文章
相關標籤/搜索