理解發布/訂閱
我們先來看一段用來模擬異步請求嵌套的代碼
let ajax = function (url, cb) {
// 省略邏輯
}
ajax('/queryOne', function (data1) {
console.log(data1)
ajax('/queryTwo' + data1.someProp, function (data2) {
console.log(data2)
ajax('/queryThree' + data2.someProp, function (data3) {
console.log(data3)
})
})
})
以上三次異步請求中:
- 內層依賴于外層,高耦合
- 不符合閱讀習慣
- 不利于調試以及排查定位問題
舉個例子,現實中大家都會微信關注公眾號,會不定期的收到推送。這個就好比我們訂閱了某一類主題,產生消息后,統一推送給所有關注了該公眾號的人。
發布/訂閱模式,屬于設計模式中的行為模式
其優點:
- 解耦
- 可伸縮性
- 高可靠性
- 可測試性
缺點:
- 無法確保消息被觸發或者觸發了幾次
是Promise方案之前的主流方案。
實現發布/訂閱
有三個要素:
- 事件發布:觸發event上綁定的函數
- 事件監聽:注冊函數到event上
- 事件池,存放 event 以及預定的函數
class EventCenter {
constructor () {
this.events = {}
}
// 發布事件 觸發對應消息的處理函數
publish (eventName, data) {
if (this.events[eventName]) {
// 一個消息會有多個訂閱者使用數組存放
this.events[eventName].forEach(callback => {
callback.apply(this, data)
})
}
}
// 訂閱事件 綁定函數與消息
subscribe (eventName, callback) {
if (this.events[eventName]) {
this.events[eventName].push(callback)
} else {
this.events[eventName] = [callback]
}
}
// 取消訂閱
unSubscribe (eventName, callback) {
if (!this.events.hasOwnProperty(eventName)) return false
// 清除該消息的所有訂閱者
if (!callback) this.events[eventName] = []
// 清除該消息的特定訂閱者
this.events[eventName] = this.events[eventName].filter(
cb => cb !== callback)
}
}
Node.js中的發布/訂閱
在Node.js中,方法默認是異步,同步方法一般會以xxxSync進行區別。
const fs = require('fs');
fs.readFile(__diraname,function(err,data) {
if(err) console.log(error);
console.log('end async read');
})
let data=fs.readFileSync(__diraname);
console.log('end sync read')
EventEmitter
核心就是事件觸發與事件監聽器功能的封裝,在Node.js中主要用于處理異步I/O操作,這個類在Node內置模塊以及第三方模塊中
const { EventEmitter } = require('events')
const event = new EventEmitter()
event.on('some_event', function (data) {
console.log('some_event 被觸發',data)
})
setTimeout(function () {
event.emit('some_event','trigger by setTimeout')
}, 3000)
// some_event 被觸發 trigger by setTimeout
常用方法:
-
addListener(event, listener)
:為指定事件添加一個監聽器到監聽器數組的尾部。 -
prependListener(event,listener)
:為指定事件添加一個監聽器到監聽器數組頭部。 -
on(event, listener)
:是addListener的別名。 -
once(event, listener)
:為指定事件注冊一個單次監聽器,即 監聽器最多只會觸發一次,觸發后立刻解除該監聽器。 -
removeListener(event, listener)
:移除指定事件的某個監聽器,監聽器必須是該事件已經注冊過的監聽器。 -
off(event,listener)
:是removeListener的別名。 -
removeAllListeners([event])
:移除所有事件的所有監聽器, 如果指定事件,則移除指定事件的所有監聽器。 -
listeners(event)
:返回指定事件的監聽器數組。 -
emit(event, [arg1], [arg2], [...])
:按監聽器的順序執行執行每個監聽器,如果事件有注冊監聽返回 true,否則返回 false。
當添加新的監聽器時,'newListener' 事件會觸發,當監聽器被移除時,'removeListener' 事件被觸發。
關于error
事件
EventEmitter 定義了一個特殊的事件 error,它包含了錯誤的語義,我們在遇到 異常的時候通常會觸發 error 事件。
當 error 被觸發時,EventEmitter 規定如果沒有響 應的監聽器,Node.js 會把它當作異常,退出程序并輸出錯誤信息。
我們一般要為會觸發 error 事件的對象設置監聽器,避免遇到錯誤后整個程序崩潰。例如:
const {EventEmitter} = require('events');
const emitter = new EventEmitter();
emitter.on('error',function(err) {
console.log("出現異常")
})
emitter.emit('error');
框架中的發布/訂閱
jQuery 的 on 和 trigger
/* 事件訂閱*/
$('#app').on('myevent', eventHandler)
// 發布
$('#app').trigger('myevent')
/* 取消訂閱*/
$('#app').off('myevent')
Vue 的雙向數據綁定
Vue 的父子組件通信 emit
觀察者模式?
觀察者模式
觀察者模式是軟件設計模式的一種。在此種模式中,一個目標對象管理所有相依于它的觀察者對象,并且在它本身的狀態改變時主動發出通知。這通常透過呼叫各觀察者所提供的方法來實現。此種模式通常被用來實時事件處理系統。 —— 維基百科
class Subject {
constructor () {
this.subscribes = []
}
on (subscriber) {
this.subscribes.push(subscriber)
}
off (subscriber) {
this.subscribes = this.subscribes.filter(sub => sub !== subscriber)
}
notify(){
this.subscribes.forEach(sub=>{
sub.update()
})
}
}
class Observer {
update(){
console.log('update...')
}
}
let subject=new Subject();
let ob1=new Observer();
let ob2=new Observer();
subject.on(ob1)
subject.on(ob2)
subject.notify()
訂閱/發布模式
在軟件架構中,發布-訂閱是一種消息范式,消息的發送者(稱為發布者)不會將消息直接發送給特定的接收者(稱為訂閱者)。而是將發布的消息分為不同的類別,無需了解哪些訂閱者(如果有的話)可能存在。同樣的,訂閱者可以表達對一個或多個類別的興趣,只接收感興趣的消息,無需了解哪些發布者(如果有的話)存在。—— 維基百科
區別
- 訂閱/發布模式,使用event-center這個中介角色來發布消息和訂閱消息,而 觀察者模式是由觀察者直接通知該消息的訂閱者(沒有中間商賺差價)
- 在發布者/訂閱者模式中,組件與觀察者模式完全分離。在觀察者模式中,主題和觀察者松散耦合。
總結
- 一般流程是,先訂閱事件,再觸發事件(但可自行實現支持先觸發 緩存后,再訂閱)
- 訂閱/發布模式 與 觀察者模式的最大區別就是 引入了 event-center