Node.js的 events 模塊功能強大,除了常規的監聽、觸發,還支持事件順序(prependListener),本文只是寫着玩玩,真正要用的話,仍是選擇成熟穩定的東西較好!html
內容概覽: 如下訂閱=監聽、發佈=觸發;通常來講,先訂閱事件,再發布事件;就像打電話同樣,電話沒撥通(訂閱),你就開始說話要幹嗎幹嗎(發佈),這時候訂閱是無效的!!!由於觸發在前、監聽在後,觸發的時候沒有監聽,監聽的時候已經結束,兩者不在一個頻道!!! 溝通就是無效的。。。node
四個功能:設計模式
// 發佈訂閱,回調函數版本
function EvtEmit() {
// 事件參數隊列
this.evtList = [];
}
複製代碼
EvtEmit.prototype = {
constructor: EvtEmit,
// 訂閱事件(監聽)
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
});
}
},
// 發佈事件(觸發)
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(`請先使用監聽on('${emitName}', callback),再emit('${emitName}')!`);
}
return;
}
// 一次性訂閱
if (evtThis.once) this.off(emitName);
// 監聽[emitName]回調的錯誤
try {
evtThis.handler(...param);
}
catch (err) {
// 不使用 on('error', callback)監聽時,打印錯誤
if (!this.evtList.some(evt => evt.emitName === 'error')) {
console.error(`on('${emitName}', callback) -> callback Error: ${err}`);
}
// 錯誤觸發,可使用 on('error', callback)監聽
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);
}
}
};
複製代碼
同正常的發佈訂閱同樣,先訂閱(on)再發布(emit);api
const evt = new EvtEmit();
// 監聽'run'事件
// 執行1次on監聽,10次回調函數
evt.on('run', res => {
console.log('res: ', res);
// 註銷監聽,如下 emit 以後將再也不觸發 on;註釋以後將無限調用
if (--res < 1) {
evt.off('run', emitName => {
console.log(`on('${emitName}')已註銷!`);
});
return;
}
evt.emit('run', res);
});
evt.emit('run', 10);
複製代碼
try/catch
執行監聽的回調函數,捕獲錯誤而後觸發emit('error', err)
,經過on('error', callback)
監聽錯誤;函數
爲何須要 try/catch?
不使用try/catch捕獲錯誤的話,一旦發生錯誤,進程就掛了,這時,後續不須要依賴你此次操做結果的 程序就會跑不下去了!!!(以下 打印 'after go',若是沒有try/catch,那麼他就不會被打印);
這在服務端用的比較多,想象一下,一個接口由於某次調用的參數不合法或者其餘因素,致使程序中斷而影響到後續使用,可能產生‘事故’!ui
// 發佈事件(觸發)
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(`請先使用監聽on('${emitName}', callback),再emit('${emitName}')!`);
}
return;
}
// 一次性訂閱
if (evtThis.once) this.off(emitName);
// 監聽[emitName]回調的錯誤
try {
evtThis.handler(...param);
}
catch (err) {
// 不使用 on('error', callback)監聽時,打印錯誤
if (!this.evtList.some(evt => evt.emitName === 'error')) {
console.error(`on('${emitName}', callback) -> callback Error: ${err}`);
}
// 錯誤觸發,可使用 on('error', callback)監聽
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 的話,不會執行
複製代碼
// EvtEmit_callback.js
// 發佈訂閱,回調函數版本
function EvtEmit() {
// 事件參數隊列
this.evtList = [];
}
EvtEmit.prototype = {
constructor: EvtEmit,
// 訂閱事件(監聽)
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
});
}
},
// 發佈事件(觸發)
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(`請先使用監聽on('${emitName}', callback),再emit('${emitName}')!`);
}
return;
}
// 一次性訂閱
if (evtThis.once) this.off(emitName);
// 監聽[emitName]回調的錯誤
try {
evtThis.handler(...param);
}
catch (err) {
// 不使用 on('error', callback)監聽時,打印錯誤
if (!this.evtList.some(evt => evt.emitName === 'error')) {
console.error(`on('${emitName}', callback) -> callback Error: ${err}`);
}
// 錯誤觸發,可使用 on('error', callback)監聽
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();
// 執行1次on監聽,10次回調函數
evt.on('run', res => {
console.log('res: ', res);
// 註銷監聽,如下 emit 以後將再也不觸發 on;註釋以後將無限調用
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 的話,不會執行
複製代碼