首頁 > 軟體

Node中的Events模組介紹及應用

2022-08-05 22:00:03

Node 中的 Events

Node 的 Events 模組只定義了一個類,就是 EventEmitter(以下簡稱 Event ),這個類在很多 Node 本身以及第三方模組中大量使用,通常是用作基礎類別被繼承。

在 Node 中,事件的應用遍及程式碼的每一個角落。

1. 事件和監聽器

Node 程式中的物件會產生一系列的事件,它們被稱為事件觸發器(event emitter),例如一個 HTTP Server 會在每次有新連線時觸發一個事件,一個 Readable Stream 會在檔案開啟時觸發一個事件等。

所有能觸發事件的物件都是 EventEmitter 類的範例。EventEmitter 定義了 on 方法,該方法的宣告如下:

emitter.on(eventName, listener)
eventName <String> | <Symbol> The name of the event.
listener <Function> The callback function

on 方法接受兩個引數:需要監聽的事件的名稱,當事件觸發時需要呼叫的函數。因為 EventEmitter 是介面,從 EventEmitter 繼承的類需要使用 new 關鍵字來構造。

觸發事件監聽器很簡單,只要呼叫 EventEmitter範例的 emit 方法就行了。需要注意的是,這些事件是針對某個範例的,不存在全域性的事件。當你呼叫 on 方法的時候,需要繫結在特定的基於 EventEmitter 的物件上。EventEmitter 類不同的範例之間也不會共用事件。

下面是一個事件註冊和觸發事件的例子。

const eventEmitter = require('events');
const myEmitter = new eventEmitter();
myEmitter.on('begin', () => {
  console.log('begin');
});
myEmitter.emit('begin');

上面的程式碼中,首先初始化了一個 EventEmitter 範例,然後註冊了一個名為 begin 的事件,之後呼叫 emit 方法觸發了這一事件。

使用者可以註冊多個同名的事件,在上面的例子中,如果註冊兩個名為 begin 的事件,那麼它們都會被觸發。

如果想獲取當前的 emitter 一共註冊了哪些事件,可以使用 eventNames 方法。

console.log(myEmitter.eventNames());

該方法會輸出包括全部事件名稱的陣列。就算註冊了兩個同名的 event,輸出結果也只有一個,說明該方法的結果集並不包含重複結果。

2. 處理 error 事件

由於 Node 程式碼執行在單執行緒環境中,那麼執行時出現的任何錯誤都有可能導致整個程序退出。利用事件機制可以實現簡單的錯誤處理功能。

當 Node 程式出現錯誤的時候,通常會觸發一個錯誤事件,如果程式碼中沒有註冊相應的處理方法,會導致 Node 程序崩潰退出。例如:

myEmitter.emit("error", new Error("crash!"));

上面的程式碼主動丟擲了一個 emor,相當於:

throw new Error ("crash");

如果我們不想因為丟擲一個 error 而使程序退出,那麼可以讓 uncaughtException 事件作為最後一道防線來捕獲異常。

const eventEmitter = require('events');
const myEmitter = new eventEmitter();
process.on('uncaughtException', () => {
  console.log('got error');
});
throw new Error('Error occurred');

這種錯誤處理的方式雖然可以捕獲異常,避免了程序的退出,但不值得提倡。

關於其常見的方法如下:

  • emitter.addListener/on(eventName, listener) :新增型別為 eventName 的監聽事件到事件陣列尾部
  • emitter.prependListener(eventName, listener):新增型別為 eventName 的監聽事件到事件陣列頭部
  • emitter.emit(eventName[, ...args]):觸發型別為 eventName 的監聽事件
  • emitter.removeListener/off(eventName, listener):移除型別為 eventName 的監聽事件
  • emitter.once(eventName, listener):新增型別為 eventName 的監聽事件,以後只能執行一次並刪除
  • emitter.removeAllListeners([eventName]): 移除全部型別為 eventName 的監聽事件

3. 繼承 Events 模組

在實際的開發中,通常不會直接使用 Event 模組來進行事件處理,而是選擇將其作為基礎類別進行繼承的方式來使用 Event,在 Node 的內部實現中,凡是提供了事件機制的模組,都會在內部繼承 Event 模組。

4. 手寫 EventEmitter

下面我們來看看如何手寫一個 EventEmitter

class EventEmitter {
  constructor() {
    this.events = {};
  }
  on(type, handler) {
    if (!this.events[type]) {
      this.events[type] = [];
    }
    this.events[type].push(handler);
  }
  addListener(type, handler) {
    this.on(type, handler)
  }
  prependListener(type, handler) {
    if (!this.events[type]) {
      this.events[type] = [];
    }
    this.events[type].unshift(handler);
  }
  removeListener(type, handler) {
    if (!this.events[type]) {
      return;
    }
    this.events[type] = this.events[type].filter(item => item !== handler);
  }
  off(type, handler) {
    this.removeListener(type, handler)
  }
  emit(type, ...args) {
    this.events[type].forEach((item) => {
      Reflect.apply(item, this, args);
    });
  }
  once(type, handler) {
    this.on(type, this._onceWrap(type, handler, this));
  }
  _onceWrap(type, handler, target) {
    const state = {
      fired: false,
      handler,
      type,
      target
    };
    const wrapFn = this._onceWrapper.bind(state);
    state.wrapFn = wrapFn;
    return wrapFn;
  }
  _onceWrapper(...args) {
    if (!this.fired) {
      this.fired = true;
      Reflect.apply(this.handler, this.target, args);
      this.target.off(this.type, this.wrapFn);
    }
  }
}

到此這篇關於Node中的Events事件介紹及應用的文章就介紹到這了,更多相關Node Events內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com