nodejs事件的監聽與事件的觸發


nodejs事件(Events)

 

一、事件機制的實現

  Node.js中大部分的模塊,都繼承自Event模塊(http://nodejs.org/docs/latest/api/events.html )。Event模塊(events.EventEmitter)是一個簡單的事件監聽器模式的實現。具有addListener/on,once,removeListener,removeAllListeners,emit等基本的事件監聽模式的方法實現。它與前端DOM樹上的事件並不相同,因為它不存在冒泡,逐層捕獲等屬於DOM的事件行為,也沒有preventDefault()、stopPropagation()、 stopImmediatePropagation() 等處理事件傳遞的方法。

   從另一個角度來看,事件偵聽器模式也是一種事件鈎子(hook)的機制,利用事件鈎子導出內部數據或狀態給外部調用者。Node.js中的很多對象,大多具有黑盒的特點,功能點較少,如果不通過事件鈎子的形式,對象運行期間的中間值或內部狀態,是我們無法獲取到的。這種通過事件鈎子的方式,可以使編程者不用關注組件是如何啟動和執行的,只需關注在需要的事件點上即可。

二、事件觸發

  events 模塊只提供了一個對象: events.EventEmitter。EventEmitter的核心就是事件發射與事件監聽器功能的封裝。EventEmitter的每個事件由一個事件名和若干個參數組成,事件名是一個字符串,通常表達一定的語義。對於每個事件,EventEmitter支持若干個事件監聽器。當事件發射時,注冊到這個事件的事件監聽器被依次調用,事件參數作為回調函數參數傳遞。

  讓我們以下面的例子解釋這個過程:

//引入事件模塊
var
events = require("events"); //創建事件監聽的一個對象 var emitter = new events.EventEmitter(); //監聽事件some_event emitter.addListener("some_event",function(){ console.log("事件觸發,調用此回調函數"); }); //觸發事件some_event emitter.emit("some_event");

 運行結果:事件觸發,調用此回調函數

 例子:

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', 'byvoid', 1991);

  運行的結果是:

  listener1 byvoid 1991

      listener2 byvoid 1991

  以上例子中,emitter 為事件 someEvent 注冊了兩個事件監聽器,然后發射了someEvent事件。運行結果中可以看到兩個事件監聽器回調函數被先后調用。這就是EventEmitter最簡單的用法。接下來我們介紹一下EventEmitter常用的API。
   EventEmitter.on(event, listener) 為指定事件注冊一個監聽器,接受一個字符串 event 和一個回調函數listener。EventEmitter.emit(event, [arg1], [arg2], [...]) 發射 event事件,傳遞若干可選參數到事件監聽器的參數表。
  EventEmitter.once(event, listener) 為指定事件注冊一個單次監聽器,即監聽器最多只會觸發一次,觸發后立刻解除該監聽器。
   EventEmitter.removeListener(event, listener) 移除指定事件的某個監聽器,listener 必須是該事件已經注冊過的監聽器。
   EventEmitter.removeAllListeners([event]) 移除所有事件的所有監聽器,如果指定 event,則移除指定事件的所有監聽器。
  更詳細的 API 文檔參見 http://nodejs.org/api/events.html

 想想其實跟jquery自定義事件很相似:

//給element綁定hello事件
element.on("hello",function(){
  alert("hello world!");
});
//觸發hello事件
element.trigger("hello");

三、事件機制的進階應用

  繼承event.EventEmitter

  實現一個繼承了EventEmitter類是十分簡單的,以下是Node.js中流對象繼承EventEmitter的例子:

var util = require("util");

var events = require("events");

//創建構造事件對象的構造函數
function Stream(){
    events.EventEmitter.call(this);
}
util.inherits(Stream, events.EventEmitter);

//實例創建事件監聽的一個對象
var elem = new Stream();

//監聽事件
elem.addListener("one_event",function(){
    console.log("事件觸發,調用此回調函數");
});

//觸發事件some_event
elem.emit("one_event");

 值得一提的是如果對一個事件添加了超過10個偵聽器,將會得到一條警告,這一處設計與Node.js自身單線程運行有關,設計者認為偵聽器太多,可能導致內存泄漏,所以存在這樣一個警告。

實例:

var util = require("util");

var events = require("events");

function Stream(){
    events.EventEmitter.call(this);
}
util.inherits(Stream, events.EventEmitter);

var elem = new Stream();

elem.addListener("我來了",function(arg1,arg2){
    console.log("事件觸發,調用此回調函數",arg1,arg2,1);
});

elem.addListener("我來了",function(arg1,arg2){
    console.log("事件觸發,調用此回調函數",arg1,arg2,2);
});

elem.addListener("我來了",function(arg1,arg2){
    console.log("事件觸發,調用此回調函數",arg1,arg2,3);
});

elem.addListener("我來了",function(arg1,arg2){
    console.log("事件觸發,調用此回調函數",arg1,arg2,4);
});

elem.addListener("我來了",function(arg1,arg2){
    console.log("事件觸發,調用此回調函數",arg1,arg2,5);
});

elem.addListener("我來了",function(arg1,arg2){
    console.log("事件觸發,調用此回調函數",arg1,arg2,6);
});

elem.addListener("我來了",function(arg1,arg2){
    console.log("事件觸發,調用此回調函數",arg1,arg2,7);
});

elem.addListener("我來了",function(arg1,arg2){
    console.log("事件觸發,調用此回調函數",arg1,arg2,8);
});

elem.addListener("我來了",function(arg1,arg2){
    console.log("事件觸發,調用此回調函數",arg1,arg2,9);
});

elem.addListener("我來了",function(arg1,arg2){
    console.log("事件觸發,調用此回調函數",arg1,arg2,10);
});
elem.addListener("我來了",function(arg1,arg2){
    console.log("事件觸發,調用此回調函數",arg1,arg2,11);
});
//觸發事件some_event
elem.emit("我來了",'one','two');

結果:

  我們通過調用emitter.setMaxListeners(0),就可以去帶哦限制

emitter.setMaxListeners(0);

  一個經典的事件監聽觸發,進程通信例子:

  master.js   

var childprocess = require('child_process');
var worker = childprocess.fork('./worker.js');

console.log('pid in master:', process.pid);

//監聽事件
worker.on('message', function(msg) {
  console.log('1:', msg);
})
process.on('message', function(msg) {
  console.log('2:', msg);
})

worker.send('---');

//觸發事件 message
process.emit('message', '------');

  worker.js

console.log('pid in worker:', process.pid);

process.on('message', function(msg) {
  console.log('3:', msg);
});

process.send('===');
process.emit('message', '======');

  運行結果:

$ node master.js


pid in master: 22229      // 主進程創建后打印其 pid
2: ------                 // 主進程收到給自己發的消息
pid in worker: 22230      // 子進程創建后打印其 pid
3: ======                 // 子進程收到給自己發的消息 
1: ===                    // 主進程收到來自子進程的消息
3: ---                    // 子進程收到來自主進程的消息

  其中有兩個有趣的點:

  在主進程中,使用 worker.on('message', ...) 監聽來自子進程的消息,使用 process.on('message', ...) 監聽給自己發的消息。但是在子進程中,只有 process.on('message', ...) 一種消息監聽方式,無法區分消息來源。

如果有給自己發消息的情況,則必須將對應的消息監聽的代碼放在消息發送代碼前面,否則無法監聽到該消息發送。例如將 master.js 的最后一行代碼 process.emit('message', '------'); 放置到該文件第一行,則運行結果不會輸出 2: ------。

如果不能控制消息監聽代碼和消息發送代碼的先后順序,可將給自己發送消息的代碼改寫為 setImmediate(process.emit.bind(process, 'message', {{message}}));

  

 參考資料:

  http://www.cnblogs.com/zhongweiv/p/nodejs_events.html(很多實例)

  http://www.infoq.com/cn/articles/tyq-nodejs-event/

  http://www.toolmao.com/nodejs-zhongwen-events-shijian

  http://www.ynpxrz.com/n691854c2023.aspx

  http://www.jb51.net/article/61079.htm

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM