nodejs事件的監聽與事件的觸發
一、事件機制的實現
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() 等處理事件傳遞的方法。
二、事件觸發
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");
一個經典的事件監聽觸發,程序通訊例子:
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
注:轉自這裡