第八章:Node 的事件處理

Node中大量運用了事件回調(diào),所以Node對事件做了單獨的封裝。所有能觸發(fā)事件的對象都是 EventEmitter 類的實例,所以上一篇我們提到的文件操作的可讀流、可寫流等都是繼承了 EventEmitter。當(dāng)然我們也可以自定義具有事件行為的自定義對象,僅需要對其繼承即可。
繼承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', '全棧實習(xí)');

錯誤處理的約定
當(dāng) EventEmitter 實例中發(fā)生錯誤時,會觸發(fā)一個 'error' 事件。 這在 Node.js 中是特殊情況。
如果 EventEmitter 沒有為 'error' 事件注冊至少一個監(jiān)聽器,則當(dāng) 'error' 事件觸發(fā)時,會拋出錯誤、打印堆棧跟蹤、且退出 Node.js 進(jìn)程。

const myEmitter = new MyEmitter();
myEmitter.emit('error', new Error('whoops!'));
// 拋出錯誤,并使 Node.js 崩潰

為了防止 Node.js 進(jìn)程崩潰,可以在 process 對象的 uncaughtException 事件上注冊監(jiān)聽器.
const myEmitter = new MyEmitter();

// 給nodejs的進(jìn)程增加未捕獲異常的處理,防止程序崩潰
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)案例
做一個應(yīng)用時,我們需要在應(yīng)用啟動之前或者啟動之后,給其他的開發(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('應(yīng)用程序出錯了!');
    });
  }

  init() {
    // 觸發(fā)預(yù)初始化事件
    this.emit('preInit');

    // ... 默認(rèn)的初始化代碼

    // 初始化事件
    this.emit('init');
  }

  start() {
    // 初始化服務(wù)器
    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ā)布訂閱(觀察者)模式。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容