參考EventEmitter實現完整訂閱發(fā)布功能函數
引言
前一篇文章 《實現一個簡單的訂閱發(fā)布功能函數|參考 EventEmitter》 實現了簡單版本的,本篇文章用 JS 完整實現 Node.js 中的 EventEmitter 。
實現
EventEmitter 中有一些重復功能的函數,或者已經移除的函數,這里不會進行實現。
主要會新增以下功能:
- 新增默認最大訂閱限制,且可進行更改
- 可獲取所有的訂閱事件名稱
- 可根據事件名獲取所有的監(jiān)聽函數
- 默認是往訂閱事件隊列尾部新增,現在新增 可往隊列頭部添加訂閱事件 的功能
根據 簡單版實現 為基礎,再根據功能來新增一些屬性和方法。
需要新增內部屬性:
maxListeners,默認最多給特定事件添加了 10 個的訂閱,如果超過了則不會生效,且會有警告提示,如果需要更多,則需要調用setMaxListeners進行設置。addListener,抽離新增訂閱的實現,用于復用
需要新增功能函數:
listeners獲取rawListeners獲取所有訂閱的原始監(jiān)聽函數listenerCount獲取所有訂閱數量eventNames獲取所有訂閱事件名prependListener從頭部新增訂閱,如果代碼中需要先執(zhí)行的訂閱,才需要用到prependOnceListener從頭部新增一次性訂閱,如果代碼中需要先執(zhí)行的訂閱,才需要用到setMaxListeners設置最大訂閱數量getMaxListeners獲取最大訂閱數量
完整代碼實現:
type Listener = (...args: any[]) => void;
type EventInfo = {
// 監(jiān)聽器
listener: Listener;
// 備份,需要改變 listener 時,則需要備份,比如 once
bak?: Listener;
};
// 創(chuàng)建一次性監(jiān)聽器
function createOnceListener(pub: PubSub, eventName: string | symbol, listener: Listener) {
const onceListener = (...args: any[]) => {
// 執(zhí)行一次后直接取消訂閱
listener(args);
pub.off(eventName, listener);
};
return onceListener;
}
export class PubSub {
private eventMap: Record<string | symbol, EventInfo[]> = {};
// 默認最多給特定事件添加了 10 個的監(jiān)聽器
private maxListeners = 10;
// 訂閱實現
private addListener = (eventName: string | symbol, listener: Listener, addToHead = false) => {
if (!this.eventMap[eventName]) {
this.eventMap[eventName] = [];
}
// 不能添加超過 maxListeners 的監(jiān)聽邏輯處理
if (this.eventMap[eventName].length >= this.maxListeners) {
console.warn(
`maxListeners: ${this.maxListeners}, ${eventName.toString()} event has add ${this.maxListeners} listener,`
);
} else {
if (addToHead) {
this.eventMap[eventName].unshift({ listener });
} else {
this.eventMap[eventName].push({ listener });
}
}
return this;
};
// 訂閱
on = (eventName: string | symbol, listener: Listener) => {
return this.addListener(eventName, listener);
};
// 取消訂閱
off = (eventName: string | symbol, listener: Listener) => {
if (this.eventMap[eventName]) {
this.eventMap[eventName] = this.eventMap[eventName].filter((item) => {
// once listener 取消訂閱
if (item.bak) {
return item.bak !== listener;
}
// 正常 listener 取消訂閱
return item.listener !== listener;
});
}
return this;
};
// 類似 EventEmitter 中的 emit 函數
emit = (eventName: string | symbol, ...args: any[]) => {
this.eventMap[eventName]?.forEach((item) => {
item.listener(...args);
});
return this;
};
// 只訂閱一次
once = (eventName: string | symbol, listener: Listener) => {
const onceListener = createOnceListener(this, eventName, listener);
this.on(eventName, onceListener);
return this;
};
// 獲取所有訂閱的原始監(jiān)聽器
listeners = (eventName: string | symbol) => {
return this.eventMap[eventName]?.map((item) => item.bak || item.listener) || [];
};
// 返回名為 eventName 的事件的監(jiān)聽器數組的副本,包括任何封裝器(例如由 .once() 創(chuàng)建的封裝器)。
rawListeners = (eventName: string | symbol) => {
return this.eventMap[eventName]?.map((item) => item.listener) || [];
};
// 獲取所有訂閱數量
listenerCount = (eventName: string | symbol) => {
return this.eventMap[eventName]?.length || 0;
};
// 獲取所有 eventName
eventNames = () => {
const eventNames = [];
for (const key in this.eventMap) {
eventNames.push(key);
}
return eventNames;
};
// 將 listener 函數添加到名為 eventName 的事件的監(jiān)聽器數組的開頭。不檢查是否已添加 listener。 多次調用傳入相同的 eventName 和 listener 組合將導致多次添加和調用 listener。
prependListener = (eventName: string | symbol, listener: Listener) => {
return this.addListener(eventName, listener, true);
};
// 將名為 eventName 的事件的單次 listener 函數添加到監(jiān)聽器數組的開頭。 下次觸發(fā) eventName 時,將移除此監(jiān)聽器,然后再調用。
prependOnceListener = (eventName: string | symbol, listener: Listener) => {
const onceListener = createOnceListener(this, eventName, listener);
this.prependListener(eventName, onceListener);
return this;
};
// 當前最大監(jiān)聽器數的值。 該值可以設置為 Infinity(或 0)以指示無限數量的監(jiān)聽器。
setMaxListeners = (n: number) => {
this.maxListeners = n;
return this;
};
// 返回當前最大監(jiān)聽器數的值,該值由 setMaxListeners(n) 設置或為默認值 10。
getMaxListeners = () => {
return this.maxListeners;
};
}
export const pubSub = new PubSub();
說明:
on、once、prependListener、prependOnceListener幾個新增listener的函數都不檢查是否已添加listener,多次調用傳入相同的eventName和listener組合將導致多次添加和調用listener,因此需要注意不要多次注入。
總結
如果你的應用非常大,需要非常精細的管理事件,那么可以使用完整版實現,如果不是的話,可以使用簡單版本。
以上就是參考EventEmitter實現完整訂閱發(fā)布功能函數的詳細內容,更多關于EventEmitter訂閱發(fā)布功能函數的資料請關注腳本之家其它相關文章!
相關文章
Mac OS X 系統(tǒng)下安裝和部署Egret引擎開發(fā)環(huán)境
之前的文章,我們已經介紹了Windows 系統(tǒng)下安裝和部署Egret的開發(fā)環(huán)境,那么,這篇文檔中,我們主要介紹在Mac環(huán)境中安裝Egret以及部署2014-09-09
Node.js+ES6+dropload.js實現移動端下拉加載實例
這個demo服務由Node搭建服務、下拉加載使用插件dropload,數據渲染應用了ES6中的模板字符串。有興趣的小伙伴可以自己嘗試下2017-06-06
詳解如何優(yōu)雅在webpack項目實現mock服務器
這篇文章主要為大家介紹了詳解如何優(yōu)雅在webpack項目實現mock服務器,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-02-02

