摘要:從異步編程解決方案說起吧事件發(fā)布訂閱模式模式流程控制庫事件發(fā)布訂閱模式事件監(jiān)聽器模式是一種廣泛運(yùn)用于異步編程的模式,是回調(diào)函數(shù)的事件話,又稱發(fā)布訂閱模式。
從node異步編程解決方案說起吧:
事件發(fā)布/訂閱模式
Promise/deferred模式
流程控制庫
事件發(fā)布/訂閱模式事件監(jiān)聽器模式是一種廣泛運(yùn)用于異步編程的模式,是回調(diào)函數(shù)的事件話,又稱發(fā)布/訂閱模式。
主要實(shí)現(xiàn)的幾個(gè)功能包括
on
remove
once
emit
廢話少說,我們來簡(jiǎn)單的實(shí)現(xiàn)一個(gè)事件監(jiān)聽函數(shù)吧
首先創(chuàng)建一個(gè)eventEmitter函數(shù)
function EventEmitter() { // 用Object.create(null)代替空對(duì)象{} // 好處是無雜質(zhì),不繼承原型鏈 // _events來保存觀察著隊(duì)列的信息 this._events = Object.create(null); }
因?yàn)檫^多的偵聽器占用大量?jī)?nèi)存,導(dǎo)致內(nèi)存泄漏,所以偵聽器的個(gè)數(shù)一般不會(huì)超過10個(gè),否則會(huì)有warnning警告??
接下來是一些默認(rèn)的設(shè)置
// 默認(rèn)最多的綁定次數(shù) EventEmitter.defaultMaxListeners = 10; // 同on方法 EventEmitter.prototype.addListener = EventEmitter.prototype.on; // 返回監(jiān)聽的事件名 EventEmitter.prototype.eventNames = function () { return Object.keys(this._events); }; // 設(shè)置最大監(jiān)聽數(shù) EventEmitter.prototype.setMaxListeners = function (n) { this._count = n; }; // 返回監(jiān)聽數(shù) EventEmitter.prototype.getMaxListeners = function () { return this._count ? this._count : this.defaultMaxListeners; };
接下來是on函數(shù)的實(shí)現(xiàn)
EventEmitter.prototype.on = function (type, cb, flag) { // 不是newListener 就應(yīng)該讓newListener執(zhí)行以下 if (type !== "newListener") { this._events["newListener"] && this._events["newListener"].forEach(listener => { listener(type); }); } if (this._events[type]) { // 根據(jù)傳入的flag來決定是向前還是向后添加 if (flag) { this._events[type].unshift(cb); } else { this._events[type].push(cb); } } else { this._events[type] = [cb]; } // 監(jiān)聽的事件不能超過了設(shè)置的最大監(jiān)聽數(shù) if (this._events[type].length === this.getMaxListeners()) { console.warn("警告-監(jiān)聽器Number過大"); } };
解析:
on函數(shù)是幫定的初始函數(shù),首先判斷是否是首次進(jìn)行偵聽,如果是的話,先進(jìn)行一遍初始化函數(shù)
接下來在——events隊(duì)列里找到指針為type的地方,根據(jù)flag判斷是在隊(duì)列尾還是頭加入callback函數(shù)
接下來是once監(jiān)聽一次的實(shí)現(xiàn)方法
// 監(jiān)聽一次 EventEmitter.prototype.once = function (type, cb, flag) { // 先綁定,調(diào)用后刪除 function wrap() { cb(...arguments); this.removeListener(type, wrap); } // 自定義屬性 wrap.listen = cb; this.on(type, wrap, flag); };
解析:
實(shí)現(xiàn)為在callback上包裝一層remove操作,再當(dāng)做一個(gè)新的callback傳入on函數(shù)
這樣的的話在首次執(zhí)行回調(diào)的時(shí)候就會(huì)執(zhí)行remove操作,達(dá)到執(zhí)行一次就刪除的操作
接下來是remove函數(shù),刪除一個(gè)type的偵聽器
EventEmitter.prototype.removeListener = function (type, cb) { if (this._events[type]) { this._events[type] = this._events[type].filter(listener => { return cb !== listener && cb !== listener.listen; }); } };
解析:
傳入type和要?jiǎng)h除的callback,對(duì)type標(biāo)記的數(shù)組進(jìn)行 filter操作,假如cb cb === listener則過濾掉
刪除所有
EventEmitter.prototype.removeAllListener = function () { this._events = Object.create(null); };
接下來是發(fā)布函數(shù) emit
EventEmitter.prototype.emit = function (type, ...args) { if (this._events[type]) { this._events[type].forEach(listener => { listener.call(this, ...args); }); } };
解析:
也比較直觀,如果events里面存在type的監(jiān)聽器隊(duì)列,則隊(duì)列里的每個(gè)回調(diào)都執(zhí)行一遍,并且用call函數(shù)綁定this和arg
//EventEmitter.js function EventEmitter() { // 用Object.create(null)代替空對(duì)象{} // 好處是無雜質(zhì),不繼承原型鏈的東東 this._events = Object.create(null); } // 默認(rèn)最多的綁定次數(shù) EventEmitter.defaultMaxListeners = 10; // 同on方法 EventEmitter.prototype.addListener = EventEmitter.prototype.on; // 返回監(jiān)聽的事件名 EventEmitter.prototype.eventNames = function () { return Object.keys(this._events); }; // 設(shè)置最大監(jiān)聽數(shù) EventEmitter.prototype.setMaxListeners = function (n) { this._count = n; }; // 返回監(jiān)聽數(shù) EventEmitter.prototype.getMaxListeners = function () { return this._count ? this._count : this.defaultMaxListeners; }; // 監(jiān)聽 EventEmitter.prototype.on = function (type, cb, flag) { // 默認(rèn)值,如果沒有_events的話,就給它創(chuàng)建一個(gè) if (!this._events) { this._events = Object.create(null); } // 不是newListener 就應(yīng)該讓newListener執(zhí)行以下 if (type !== "newListener") { this._events["newListener"] && this._events["newListener"].forEach(listener => { listener(type); }); } if (this._events[type]) { // 根據(jù)傳入的flag來決定是向前還是向后添加 if (flag) { this._events[type].unshift(cb); } else { this._events[type].push(cb); } } else { this._events[type] = [cb]; } // 監(jiān)聽的事件不能超過了設(shè)置的最大監(jiān)聽數(shù) if (this._events[type].length === this.getMaxListeners()) { console.warn("警告-警告-警告"); } }; // 向前添加 EventEmitter.prototype.prependListener = function (type, cb) { this.on(type, cb, true); }; EventEmitter.prototype.prependOnceListener = function (type, cb) { this.once(type, cb, true); }; // 監(jiān)聽一次 EventEmitter.prototype.once = function (type, cb, flag) { // 先綁定,調(diào)用后刪除 function wrap() { cb(...arguments); this.removeListener(type, wrap); } // 自定義屬性 wrap.listen = cb; this.on(type, wrap, flag); }; // 刪除監(jiān)聽類型 EventEmitter.prototype.removeListener = function (type, cb) { if (this._events[type]) { this._events[type] = this._events[type].filter(listener => { return cb !== listener && cb !== listener.listen; }); } }; EventEmitter.prototype.removeAllListener = function () { this._events = Object.create(null); }; // 返回所有的監(jiān)聽類型 EventEmitter.prototype.listeners = function (type) { return this._events[type]; }; // 發(fā)布 EventEmitter.prototype.emit = function (type, ...args) { if (this._events[type]) { this._events[type].forEach(listener => { listener.call(this, ...args); }); } }; module.exports = EventEmitter;
我的博客即將同步至騰訊云+社區(qū),邀請(qǐng)大家一同入駐:https://cloud.tencent.com/dev...
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/103329.html
摘要:為什么把叫做集合而不能稱為嚴(yán)格意義上的對(duì)象,來看這個(gè)集合的構(gòu)造函數(shù)可以見得,是與處于同一層級(jí)的而非是繼承自,所以說由實(shí)例出來的對(duì)象更加的純凈,并沒有諸如等方法,更像是一個(gè)集合。 寫在前面 事件的編程方式具有輕量級(jí)、松耦合、只關(guān)注事務(wù)點(diǎn)等優(yōu)勢(shì),在瀏覽器端,有著自己的一套DOM事件機(jī)制,其中含包括這諸如事件冒泡,事件捕獲等;然而Node的事件機(jī)制沒有事件冒泡等,其原理就是設(shè)計(jì)模式中的觀察者...
摘要:觀察者模式觀察者模式廣泛的應(yīng)用于語言中,瀏覽器事件如鼠標(biāo)單擊,鍵盤事件都是該模式的例子。可以看到,這就是觀察者模式的訂閱方法實(shí)現(xiàn)。小結(jié)通過創(chuàng)建可觀察的對(duì)象,當(dāng)發(fā)生一個(gè)感興趣的事件時(shí)可將該事件通告給所有觀察者,從而形成松散的耦合。 觀察者模式 觀察者模式(observer)廣泛的應(yīng)用于javascript語言中,瀏覽器事件(如鼠標(biāo)單擊click,鍵盤事件keyDown)都是該模式的例子。...
摘要:為指定事件注冊(cè)一個(gè)單次監(jiān)聽器,即監(jiān)聽器最多只會(huì)觸發(fā)一次,觸發(fā)后立刻解除該監(jiān)聽器。移除指定事件的某個(gè)監(jiān)聽器,監(jiān)聽器必須是該事件已經(jīng)注冊(cè)過的監(jiān)聽器。返回指定事件的監(jiān)聽器數(shù)組。如何創(chuàng)建空對(duì)象我們已經(jīng)了解到,是要來儲(chǔ)存監(jiān)聽事件監(jiān)聽器數(shù)組的。 毫無疑問,nodeJS改變了整個(gè)前端開發(fā)生態(tài)。本文通過分析nodeJS當(dāng)中events模塊源碼,由淺入深,動(dòng)手實(shí)現(xiàn)了屬于自己的ES6事件觀察者系統(tǒng)。千萬不...
摘要:發(fā)布訂閱模式訂閱者把自己想訂閱的事件注冊(cè)到調(diào)度中心,當(dāng)發(fā)布者發(fā)布該事件到調(diào)度中心,也就是該事件觸發(fā)時(shí),由調(diào)度中心統(tǒng)一調(diào)度訂閱者注冊(cè)到調(diào)度中心的處理代碼。 發(fā)布-訂閱模式,看似陌生,其實(shí)不然。工作中經(jīng)常會(huì)用到,例如 Node.js EventEmitter 中的 on 和 emit 方法;Vue 中的 $on 和 $emit 方法。他們都使用了發(fā)布-訂閱模式,讓開發(fā)變得更加高效方便。 一...
摘要:實(shí)例方法的話,最核心的就是分別是添加事件,刪除事件,發(fā)布事件。為了防止進(jìn)程崩潰,可以在對(duì)象的事件上注冊(cè)監(jiān)聽器,或使用模塊。注意,模塊已被廢棄。作為最佳實(shí)踐,應(yīng)該始終為事件注冊(cè)監(jiān)聽器。 前言 事件在js中非常的常見,不管是瀏覽器還是node,這種事件發(fā)布/訂閱模式的應(yīng)用都是很常見的。至于發(fā)布/訂閱模式和觀察者模式是否是同一種設(shè)計(jì)模式說法都有,這里不做具體的討論。在之前的項(xiàng)目中也曾自己實(shí)現(xiàn)...
閱讀 1276·2021-11-24 09:39
閱讀 1533·2021-09-07 09:59
閱讀 3490·2019-08-30 15:54
閱讀 2486·2019-08-30 11:00
閱讀 2678·2019-08-29 15:06
閱讀 2169·2019-08-26 13:52
閱讀 438·2019-08-26 13:24
閱讀 2505·2019-08-26 12:20