Node中大量運用了事件回調(diào)肛根,所以Node對事件做了單獨的封裝逗鸣。所有能觸發(fā)事件的對象都是 EventEmitter
類的實例,所以上一篇我們提到的文件操作的可讀流盲再、可寫流等都是繼承了 EventEmitter
框全。當然我們也可以自定義具有事件行為的自定義對象,僅需要對其繼承即可汁针。
繼承EventEmitter
node的events
模塊封裝了EventEmitter
類型术辐,此類型里面封裝了事件注冊、觸發(fā)等API施无。
// 引入events模塊
const EventEmitter = require('events');
class MyEmitter extends EventEmitter {
constructor(opt) {
super(opt);
}
}
// 創(chuàng)建事件對象實例辉词。
const myEmitter = new MyEmitter();
// 注冊event事件,event是事件名字猾骡,最好符合以駝峰命名規(guī)范瑞躺。
myEmitter.on('event', () => {
console.log('觸發(fā)了一個事件!');
});
// 觸發(fā)event事件
myEmitter.emit('event');
給回調(diào)函數(shù)傳遞參數(shù)
emit()
方法觸發(fā)事件的同時兴想,還可以給回調(diào)函數(shù)傳遞參數(shù)幢哨。
// 引入events模塊
const EventEmitter = require('events');
class MyEmitter extends EventEmitter {
constructor(opt) {
super(opt);
}
}
// 創(chuàng)建事件對象實例。
const myEmitter = new MyEmitter();
// 注冊event事件嫂便,event是事件名字捞镰,最好符合以駝峰命名規(guī)范。
myEmitter.on('event', (a, b) => {
console.log('event: %s, %s', a, b);
});
// 觸發(fā)event事件毙替,并傳遞參數(shù)a曼振、b
myEmitter.emit('event', 'aicoder.com', '全棧實習');
錯誤處理的約定
當 EventEmitter 實例中發(fā)生錯誤時,會觸發(fā)一個 'error' 事件蔚龙。 這在 Node.js 中是特殊情況。
如果 EventEmitter 沒有為 'error' 事件注冊至少一個監(jiān)聽器映胁,則當 'error' 事件觸發(fā)時木羹,會拋出錯誤、打印堆棧跟蹤、且退出 Node.js 進程坑填。
const myEmitter = new MyEmitter();
myEmitter.emit('error', new Error('whoops!'));
// 拋出錯誤抛人,并使 Node.js 崩潰
為了防止 Node.js 進程崩潰,可以在 process
對象的 uncaughtException
事件上注冊監(jiān)聽器.
const myEmitter = new MyEmitter();
// 給nodejs的進程增加未捕獲異常的處理脐瑰,防止程序崩潰
process.on('uncaughtException', (err) => {
console.error('有錯誤');
});
myEmitter.emit('error', new Error('whoops!'));
// 打印: 有錯誤
只處理事件一次
on()
方法可以注冊事件處理程序妖枚,而且是每次emit()
觸發(fā)事件,都會被執(zhí)行苍在。但是用once()
注冊的事件绝页,僅執(zhí)行一次。例如:
// 引入events模塊
const EventEmitter = require('events');
class MyEmitter extends EventEmitter {
constructor(opt) {
super(opt);
}
}
const myEmitter = new MyEmitter();
let m = 0;
myEmitter.once('event', () => {
console.log(++m);
});
myEmitter.emit('event');
// 打印: 1
myEmitter.emit('event');
// 忽略
實戰(zhàn)案例
做一個應用時寂恬,我們需要在應用啟動之前或者啟動之后续誉,給其他的開發(fā)人員提供一些可以注冊處理程序的鉤子,可以用事件的方式實現(xiàn)初肉。其實本質(zhì)就是發(fā)布訂閱模式酷鸦。
'use strict';
const EventEmitter = require('events');
class Application extends EventEmitter {
constructor(opt) {
super(opt);
this.on('error', err => {
console.log('應用程序出錯了!');
});
}
init() {
// 觸發(fā)預初始化事件
this.emit('preInit');
// ... 默認的初始化代碼
// 初始化事件
this.emit('init');
}
start() {
// 初始化服務器
this.init();
}
}
/**/
var app = new Application();
app.on('init', () => {
console.log('初始化牙咏!');
});
app.on('preInit', () => {
console.log('pre init');
});
app.start();
總結(jié)
Node中的事件處理封裝很簡單易用臼隔,跟jQuery的事件系統(tǒng)非常類似。其實自己實現(xiàn)一套事件系統(tǒng)也不難妄壶,核心思想就是:發(fā)布訂閱(觀察者)模式摔握。