定义:
基于一个事件(主题)通道,希望接收通知的对象 Subscriber 通过自定义事件订阅主题,被激活事件的对象 Publisher 通过发布主题事件的方式通知各个订阅该主题的 Subscriber 对象(发布-订阅者模式只是观察者模式的别名,随着时间推移,在观察者基础上,发展成一种不同的设计模式,功能更强大)
场景:
1.当一个对象的状态变化需要通知其他多个对象时,可以使用发布订阅模式来实现松耦合的通信
2.当一个事件或消息需要广泛传播或分发给多个接收者时,可以使用发布订阅模式来实现高效的消息分发
3.当一个系统需要支持异步处理或批量处理时,可以使用发布订阅模式来实现事件的延迟触发或批量触发
优点(比较与观察者模式):
1.可以一对多也可以多对多
2.主体之间松耦合
3.可以并行操作
4.多用于异步操作
缺点:
1.当消息事件中间件采用定时发布通知时,订阅发布模式无法确认所有的订阅者都收到了消息
2.当负载激增,请求订阅的订阅者数量增加,每个订阅者接收到通知的速度将会变慢
与观察者模式区别
1.观察者模式内部维护了观察者,知道有哪些观察者的存在,而发布/订阅模式则省略了这一步骤,直接维护订阅者的事件机制,不用管谁在订阅
2.发布/订阅模式相比观察者模式多了一个中间媒介,有了这个中间媒介之后,发布者和订阅者的关联就更加的松耦合
3.观察者模式通常用于同步的场景, 而发布/订阅模式大多用于异步的场景,如消息队列
具体实现
class Store {
constructor () {
this.repertory = {}
}
$on (name, fn) {
let that = this
if (Array.isArray(name)) {
name.forEach(nme => {
that.$on(nme, fn)
})
} else {
(that.repertory[name] || (that.repertory[name] = [])).push(fn)
}
}
$emit (name) {
let that = this
let args = Array.from(arguments)
let cbs = this.repertory[name]
if (!cbs || cbs.length == 0) {
throw Error(`xxx`)
}
cbs.forEach(cb => {
cb.call(that, ...args)
})
return that
}
$off (name, fn) {
let that = this
if (!arguments.length || arguments.length == 0) {
that.repertory = Object.create(null)
return that
}
if (Array.isArray(name)) {
name.forEach(nme => {
that.$off(nme, fn)
})
return that
}
let cbs = that.repertory[name]
if (!cbs) {
throw Error('xxx')
}
if (!fn) {
that.repertory[name] = null
return that
}
let cb
let i = cbs.length
while (i--) {
cb = cbs[i]
if (cb == fn || cb.fn == fn) {
cbs.splice(i, 1)
break
}
}
}
$once (name, fn) {
let that = this
function on () {
that.$off(name, on)
fn.apply(that, arguments)
}
on.fn = fn
that.$on(name, on)
}
}