摘要:前言的模塊功能強大,除了常規(guī)的監(jiān)聽觸發(fā),還支持事件順序,本文只是寫著玩玩,真正要用的話,還是選擇成熟穩(wěn)定的東西較好內(nèi)容概覽以下訂閱監(jiān)聽發(fā)布觸發(fā)一般來說,先訂閱事件,再發(fā)布事件就像打電話一樣,電話沒撥通訂閱,你就開始說話要干嘛干嘛發(fā)布,這時候
前言Node.js的 events 模塊功能強大,除了常規(guī)的監(jiān)聽、觸發(fā),還支持事件順序(prependListener),本文只是寫著玩玩,真正要用的話,還是選擇成熟穩(wěn)定的東西較好!
內(nèi)容概覽: 以下訂閱=監(jiān)聽、發(fā)布=觸發(fā);一般來說,先訂閱事件,再發(fā)布事件;就像打電話一樣,電話沒撥通(訂閱),你就開始說話要干嘛干嘛(發(fā)布),這時候訂閱是無效的!!!因為觸發(fā)在前、監(jiān)聽在后,觸發(fā)的時候沒有監(jiān)聽,監(jiān)聽的時候已經(jīng)結(jié)束,二者不在一個頻道!!! 溝通就是無效的。。。
四個功能:
訂閱on
訂閱once(一次性
發(fā)布emit
注銷off
錯誤監(jiān)聽error
構(gòu)造函數(shù)// 發(fā)布訂閱,回調(diào)函數(shù)版本
function EvtEmit() {
// 事件參數(shù)隊列
this.evtList = [];
}
原型
EvtEmit.prototype = {
constructor: EvtEmit,
// 訂閱事件(監(jiān)聽)
on(emitName, handler) {
// console.debug(`EvtEmit -- on: ${emitName}`);
if (!emitName) return;
if (!this.evtList.some(evt => evt.emitName === emitName)) {
this.evtList.push({
emitName, // 事件名稱
handler,
once: emitName === "error" ");true : false
});
}
},
// 訂閱事件(一次性
once(emitName, handler) {
if (!emitName) return;
if (!this.evtList.some(evt => evt.emitName === emitName)) {
this.evtList.push({
emitName, // 事件名稱
handler,
once: true
});
}
},
// 發(fā)布事件(觸發(fā))
emit(emitName, ...param) {
// console.debug(`EvtEmit -- emit: ${emitName}`);
if (!emitName) return;
let evtThis = this.evtList.find(evt => evt.emitName === emitName);
if (!evtThis) {
if (emitName !== "error") {
console.warn(`請先使用監(jiān)聽on("${emitName}", callback),再emit("${emitName}")!`);
}
return;
}
// 一次性訂閱
if (evtThis.once) this.off(emitName);
// 監(jiān)聽[emitName]回調(diào)的錯誤
try {
evtThis.handler(...param);
}
catch (err) {
// 不使用 on("error", callback)監(jiān)聽時,打印錯誤
if (!this.evtList.some(evt => evt.emitName === "error")) {
console.error(`on("${emitName}", callback) -> callback Error: ${err}`);
}
// 錯誤觸發(fā),可以使用 on("error", callback)監(jiān)聽
this.emit("error", {
emitName,
err
});
}
},
// 注銷事件訂閱
off(emitName, callback = null) {
let arr = this.evtList.filter(evt => evt.emitName !== emitName);
this.evtList = arr;
arr = null;
if (callback) {
callback.call(this, emitName);
}
}
};
使用
同正常的發(fā)布訂閱一樣,先訂閱(on)再發(fā)布(emit);
const evt = new EvtEmit();
// 監(jiān)聽"run"事件
// 執(zhí)行1次on監(jiān)聽,10次回調(diào)函數(shù)
evt.on("run", res => {
console.log("res: ", res);
// 注銷監(jiān)聽,以下 emit 之后將不再觸發(fā) on;注釋之后將無限調(diào)用
if (--res < 1) {
evt.off("run", emitName => {
console.log(`on("${emitName}")已注銷!`);
});
return;
}
evt.emit("run", res);
});
evt.emit("run", 10);
輸出
錯誤監(jiān)聽
try/catch執(zhí)行監(jiān)聽的回調(diào)函數(shù),捕獲錯誤然后觸發(fā)emit("error", err),通過on("error", callback)監(jiān)聽錯誤;
try/catch 包裹回調(diào)函數(shù)的執(zhí)行為什么需要 try/catch");
不使用try/catch捕獲錯誤的話,一旦發(fā)生錯誤,進(jìn)程就掛了,這時,后續(xù)不需要依賴你這次操作結(jié)果的 程序就會跑不下去了!!!(如下 打印 "after go",如果沒有try/catch,那么他就不會被打印);
這在服務(wù)端用的比較多,想象一下,一個接口因為某次調(diào)用的參數(shù)不合法或者其他因素,導(dǎo)致程序中斷而影響到后續(xù)使用,可能產(chǎn)生‘事故’!
// 發(fā)布事件(觸發(fā))
emit(emitName, ...param) {
// console.debug(`EvtEmit -- emit: ${emitName}`);
if (!emitName) return;
let evtThis = this.evtList.find(evt => evt.emitName === emitName);
if (!evtThis) {
if (emitName !== "error") {
console.warn(`請先使用監(jiān)聽on("${emitName}", callback),再emit("${emitName}")!`);
}
return;
}
// 一次性訂閱
if (evtThis.once) this.off(emitName);
// 監(jiān)聽[emitName]回調(diào)的錯誤
try {
evtThis.handler(...param);
}
catch (err) {
// 不使用 on("error", callback)監(jiān)聽時,打印錯誤
if (!this.evtList.some(evt => evt.emitName === "error")) {
console.error(`on("${emitName}", callback) -> callback Error: ${err}`);
}
// 錯誤觸發(fā),可以使用 on("error", callback)監(jiān)聽
this.emit("error", {
emitName,
err
});
}
},
錯誤捕獲:
evt.on("error", ({ emitName, err }) => {
console.error(`on("${emitName}", callback) -> callback Error: ${err}`);
})
evt.on("go", res => {
err; // 錯誤會被 try/catch 捕獲
console.log("res: ", res);
});
evt.emit("go", "go");
console.log("after go"); // 沒有 try/catch 的話,不會執(zhí)行
全部代碼 EvtEmit_callback.js:
// EvtEmit_callback.js
// 發(fā)布訂閱,回調(diào)函數(shù)版本
function EvtEmit() {
// 事件參數(shù)隊列
this.evtList = [];
}
EvtEmit.prototype = {
constructor: EvtEmit,
// 訂閱事件(監(jiān)聽)
on(emitName, handler) {
// console.debug(`EvtEmit -- on: ${emitName}`);
if (!emitName) return;
if (!this.evtList.some(evt => evt.emitName === emitName)) {
this.evtList.push({
emitName, // 事件名稱
handler,
once: emitName === "error" ");true : false
});
}
},
// 訂閱事件(一次性
once(emitName, handler) {
if (!emitName) return;
if (!this.evtList.some(evt => evt.emitName === emitName)) {
this.evtList.push({
emitName, // 事件名稱
handler,
once: true
});
}
},
// 發(fā)布事件(觸發(fā))
emit(emitName, ...param) {
// console.debug(`EvtEmit -- emit: ${emitName}`);
if (!emitName) return;
let evtThis = this.evtList.find(evt => evt.emitName === emitName);
if (!evtThis) {
if (emitName !== "error") {
console.warn(`請先使用監(jiān)聽on("${emitName}", callback),再emit("${emitName}")!`);
}
return;
}
// 一次性訂閱
if (evtThis.once) this.off(emitName);
// 監(jiān)聽[emitName]回調(diào)的錯誤
try {
evtThis.handler(...param);
}
catch (err) {
// 不使用 on("error", callback)監(jiān)聽時,打印錯誤
if (!this.evtList.some(evt => evt.emitName === "error")) {
console.error(`on("${emitName}", callback) -> callback Error: ${err}`);
}
// 錯誤觸發(fā),可以使用 on("error", callback)監(jiān)聽
this.emit("error", {
emitName,
err
});
}
},
// 注銷事件訂閱
off(emitName, callback = null) {
let arr = this.evtList.filter(evt => evt.emitName !== emitName);
this.evtList = arr;
arr = null;
if (callback) {
callback.call(this, emitName);
}
}
};
const evt = new EvtEmit();
// 執(zhí)行1次on監(jiān)聽,10次回調(diào)函數(shù)
evt.on("run", res => {
console.log("res: ", res);
// 注銷監(jiān)聽,以下 emit 之后將不再觸發(fā) on;注釋之后將無限調(diào)用
if (--res < 1) {
evt.off("run", emitName => {
console.log(`on("${emitName}")已注銷!`);
});
return;
}
evt.emit("run", res);
});
evt.emit("run", 10);
// evt.on("error", ({ emitName, err }) => {
// console.error(`on("${emitName}", callback) -> callback Error: ${err}`);
// })
// evt.on("go", res => {
// err; // 錯誤會被 try/catch 捕獲
// console.log("res: ", res);
// });
// evt.emit("go", "go");
// console.log("after go"); // 沒有 try/catch 的話,不會執(zhí)行
參考
js設(shè)計模式之發(fā)布/訂閱模式模式
events(事件觸發(fā)器)
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/6776.html
摘要:觀察者模式定義設(shè)計模式中對的定義一個對象稱為維持一系列依賴于它觀察者的對象,將有關(guān)狀態(tài)的任何變更自動通知給它們。如圖模式比較觀察者模式則多了一個類似于話題調(diào)度中心的流程,發(fā)布者和訂閱者解耦。 Obeserver(觀察者)模式 定義 《js設(shè)計模式》中對Observer的定義:一個對象(稱為subject)維持一系列依賴于它(觀察者)的對象,將有關(guān)狀態(tài)的任何變更自動通知給它們。 《設(shè)計模...
摘要:設(shè)計模式與開發(fā)實踐讀書筆記。發(fā)布訂閱模式又叫觀察者模式,它定義了對象之間的一種一對多的依賴關(guān)系。附設(shè)計模式之發(fā)布訂閱模式觀察者模式數(shù)據(jù)結(jié)構(gòu)和算法系列棧隊列優(yōu)先隊列循環(huán)隊列設(shè)計模式系列設(shè)計模式之策略模式 《JavaScript設(shè)計模式與開發(fā)實踐》讀書筆記。 發(fā)布-訂閱模式又叫觀察者模式,它定義了對象之間的一種一對多的依賴關(guān)系。當(dāng)一個對象的狀態(tài)發(fā)生改變時,所有依賴它的對象都將得到通知。 例...
摘要:設(shè)計模式與開發(fā)實踐讀書筆記。看此文章前,建議先看設(shè)計模式之發(fā)布訂閱模式觀察者模式在中,已經(jīng)介紹了什么是發(fā)布訂閱模式,同時,也實現(xiàn)了發(fā)布訂閱模式。 《JavaScript設(shè)計模式與開發(fā)實踐》讀書筆記。 看此文章前,建議先看JavaScript設(shè)計模式之發(fā)布-訂閱模式(觀察者模式)-Part1 在Part1中,已經(jīng)介紹了什么是發(fā)布-訂閱模式,同時,也實現(xiàn)了發(fā)布-訂閱模式。但是,就Part1...
閱讀 2643·2021-11-22 15:24
閱讀 1376·2021-11-17 09:38
閱讀 2752·2021-10-09 09:57
閱讀 1201·2019-08-30 15:44
閱讀 2444·2019-08-30 14:00
閱讀 3545·2019-08-30 11:26
閱讀 2938·2019-08-29 16:28
閱讀 752·2019-08-29 13:56