讀了 events
模塊的文檔,研究了幾個有意思的問題:javascript
引用/轉載 請聲明出處: 原文連接: xxoo521.com
Nodejs 使用了一個事件驅動、非阻塞 IO 的模型。events
模塊是事件驅動的核心模塊。不少內置模塊都繼承了events.EventEmitter
。html
本身無需手動實現這種設計模式,直接繼承EventEmitter
便可。代碼以下:前端
const { EventEmitter } = require("events"); class MyEmitter extends EventEmitter {} const ins = new MyEmitter(); ins.on("test", () => { console.log("emit test event"); }); ins.emit("test");
根據文檔,應該 EventEmitter 實例的error
事件是個特殊事件。推薦作法是:在建立實例後,應該當即註冊error
事件。java
const ins = new MyEmitter(); ins.on("error", error => { console.log("error msg is", error.message); });
註冊error
事件後,我本來的理解是,全部事件回掉邏輯中的錯誤都會在 EventEmitter 內部被捕獲,而且在內部觸發 error
事件。node
也就是說下面代碼,會打印:"error msg is a is not defined"。git
ins.on("test", () => { console.log(a); }); ins.emit("test");
然而,錯誤並無捕獲,直接拋出了異常。因而可知,EventEmitter 在執行內部邏輯的時候,並無try-catch
。這個緣由,請見Node Issue。簡單來說,Error 和 Exception 並不徹底同樣。github
若是按照正常想法,不想每一次都在外面套一層try-catch
,那應該怎麼作呢?個人作法是在
EventEmitter 原型鏈上新增一個safeEmit
函數。算法
EventEmitter.prototype.safeEmit = function(name, ...args) { try { return this.emit(name, ...args); } catch (error) { return this.emit("error", error); } };
如此一來,運行前一段代碼的 Exception 就會被捕獲到,而且觸發error
事件。前一段代碼的輸出就變成了:設計模式
error msg is a is not defined
對於同一個事件,觸發它的時候,函數的執行順序就是函數綁定時候的順序。官方庫提供了emitter.prependListener()
和 emitter.prependOnceListener()
兩個接口,可讓新的監聽器直接添加到隊列頭部。api
可是若是想讓新的監聽器放入任何監聽器隊列的任何位置呢?在原型鏈上封裝了 insertListener
方法。
EventEmitter.prototype.insertListener = function( name, index, callback, once = false ) { // 若是是once監聽器,其數據結構是 {listener: Function} // 正常監聽器,直接是 Function const listeners = ins.rawListeners(name); const that = this; // 下標不合法 if (index > listeners.length || index < 0) { return false; } // 綁定監聽器數量已達上限 if (listeners.length >= this.getMaxListeners()) { return false; } listeners.splice(index, 0, once ? { listener: callback } : callback); this.removeAllListeners(name); listeners.forEach(function(item) { if (typeof item === "function") { that.on(name, item); } else { const { listener } = item; that.once(name, listener); } }); return true; };
使用起來,效果以下:
const ins = new MyEmitter(); ins.on("error", error => { console.log("error msg is", error.message); }); ins.on("test", () => { console.log("test 1"); }); ins.on("test", () => { console.log("test 2"); }); // 監聽器隊列中插入新的監聽器,一個是once類型,一個不是once類型 ins.insertListener( "test", 0, () => { console.log("once test insert"); }, true ); ins.insertListener("test", 1, () => { console.log("test insert"); });
連續調用兩次ins.emit("test")
,結果輸出以下:
# 第一次 once test insert test insert test 1 test 2 # 第二次: once 類型的監聽器調用一次後銷燬 test insert test 1 test 2
在綁定事件監聽器的時候,若是監聽器沒有被 remove,那麼存在內存泄漏的風險。
我知道的常見作法以下:
once
綁定監聽器,調用一次後,監聽器被自動移除EventEmitter
專一前端與算法的系列乾貨分享,歡迎關注(¬‿¬)