Node中的Events模塊介紹及應用
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內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- Node.js原理阻塞和EventEmitter及其繼承的運用實戰
- JS中自定義事件與觀察者模式詳解
- TypeScript實現類型安全的EventEmitter
- node.js核心模塊有哪些
- Node.js自定義對象事件的監聽與發射