Skip to content

发布订阅模式

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('不开心')

总结:发布订阅没有直接关联,可以只订阅,不发布,也可以只发布,不订阅。 观察者模式会有关联,被观察者存储着观察者列表,被观察者触发事件时,会通知所有观察者。