发布订阅模式
js
// 可以添加事件侦听器,触发事件,并处理异步事件
class EventEmitter {
constructor() {
this.events = {}
}
// 添加事件侦听器
on(eventName, listener) {
if (!this.events[eventName]) {
this.events[eventName] = []
}
this.events[eventName].push(listener)
}
// 触发事件
emit(eventName, ...args) {
if (this.events[eventName]) {
this.events[eventName].forEach((listener) => {
listener(...args)
})
}
}
// 添加一次性事件
once(eventName, listener) {
const onceWrapper = (...args) => {
listener(...args)
this.off(eventName, onceWrapper)
}
this.on(eventName, onceWrapper)
}
// 移除事件
off(eventName, listenerToRemove) {
if (this.events[eventName]) {
this.events[eventName] = this.events[eventName].filter((listener) => {
return listener !== listenerToRemove
})
}
}
// 处理异步事件
async emitAsync(eventName, ...args) {
if (this.events[eventName]) {
// Use Promise.all to wait for all listeners to resolve
await Promise.all(this.events[eventName].map(listener => listener(...args)))
}
}
}
js
// 示例用法
const emitter = new EventEmitter()
// 添加事件侦听器
emitter.on('event1', () => {
console.log('event1 emitted')
})
// 触发事件
emitter.emit('event1') // 输出: event1 emitted
// 添加一次性事件
emitter.once('event2', () => {
console.log('event2 emitted once')
})
// 触发事件
emitter.emit('event2') // 输出: event2 emitted once
emitter.emit('event2') // 不会再输出,因为是一次性事件
// 移除事件
function event3Handler() {
console.log('event3 emitted')
}
emitter.on('event3', event3Handler)
emitter.emit('event3') // 输出: event3 emitted
emitter.off('event3', event3Handler)
emitter.emit('event3') // 不会再输出,因为事件已被移除
// 处理异步事件
emitter.on('asyncEvent', async () => {
await new Promise(resolve => setTimeout(resolve, 1000))
console.log('asyncEvent handled')
})
emitter.emitAsync('asyncEvent') // 等待1秒后输出: asyncEvent handled
异步文件读取案例
js
const fs = require('node:fs')
const event = {
_arr: [],
on(fn) {
_arr.push(fn)
},
emit() {
_arr.forEach(item => item())
}
}
const person = {}
event.on(() => {
console.log('订阅者1')
console.log(person)
})
event.on(() => {
console.log('订阅者2')
if (Object.keys(person).length === 2)
console.log(person)
})
fs.readFile('./name.txt', (_, data) => {
person.name = data
event.emit()
})
fs.readFile('./age.txt', (_, data) => {
person.age = data
event.emit()
})
观察者模式
js
// 观察者对象
class Observer {
constructor(name) {
this.name = name
}
update(newValue) {
log(`${this.name} 更新了,新值为 ${newValue}`)
}
}
// 主题对象
class Subject {
constructor() {
this.state = '开心'
this.arr = []
}
add(observer) {
this.arr.push(observer)
}
// 移除观察者
remove(observer) {
this.arr = this.arr.filter(obs => obs !== observer)
}
setState(newValue) {
this.state = newValue
this.notify()
}
notify() {
this.arr.forEach(item => item.update(this.state))
}
}
const subject = new Subject()
const observer1 = new Observer('张三')
const observer2 = new Observer('李四')
subject.add(observer1)
subject.add(observer2)
subject.setState('不开心')
总结:发布订阅没有直接关联,可以只订阅,不发布,也可以只发布,不订阅。 观察者模式会有关联,被观察者存储着观察者列表,被观察者触发事件时,会通知所有观察者。