如何写一个 简单的 发布订阅模式 demo

425 阅读1分钟

如何写一个 简单的 发布订阅模式 demo

发布订阅模式 是一个很常见的 异步编程模式。 主要是通过把 回调函数 事件化来实现该模式

  • 订阅者
  • 发布者
  • 信号中心

关于这 3 者的意思呢,网上众说纷纭。我感觉怎么理解都是对的。

那就写一个可以 可以收集订阅者注册的事件,并且可以通知订阅者的 dome

开始

  1. 这里我们用 class 表示。并初始化一个 subs 用于存放 事件 的对象
class EventEmitter {
  constructor() {
    this.subs = Object.create(null)
  }
}
  1. subs 设计成一个对象,把 作为事件名称, 为一个数组用于存放事件动作。如下所示
type Subs = { [key: string]: [Function] }

$on 订阅事件/注册事件

  • 如何使用

第一个参数是 事件名称 ,第二个参数是 处理函数

function handleClick() {
  console.log('clicked')
}
em.$on('click', handleClick)
  • 如何实现

事件名称 作为 key,赋值为一个数组。并把处理函数放入数组中

  $on (eventType, handler) {
    this.subs[eventType] = this.subs[eventType] || []
    this.subs[eventType].push(handler)
  }

$emit 发布通知/触发事件

  • 如何使用

很简单只需要传入要触发的事件名称即可

em.$emit('click')
  • 如何实现

事件名称 获取到 subs 中对应的 事件动作 集合。并循环执行即可

  $emit (eventType) {
    this.subs[eventType].forEach(handler => handler())
  }

完整代码

class EventEmitter {
  constructor() {
    this.subs = Object.create(null)
  }
  $on (eventType, handler) {
    this.subs[eventType] = this.subs[eventType] || []
    this.subs[eventType].push(handler)
  }
  $emit (eventType) {
    this.subs[eventType].forEach(handler => handler())
  }
}

// Test
const em = new EventEmitter()

function handleClick () {
  console.log('clicked')
}
em.$on('click', handleClick)

em.$on('click', () => {
  console.log('clicked2')
})
em.$on('change', () => {
  console.log('changed')
})

em.$emit('click') // > clicked clicked2
em.$emit('change') // > changed

触发事件时,传入参数

当我们想在 发布通知/触发事件 告诉订阅者,点击的是谁、改变后的值是什么。这时候我们就需要在 触发 时可以传入参数

  • 如何使用
em.$emit('eventName', 'params1', 'params1') // > clicked clicked2
  • 如何实现

修改下 $emit 代码

  $emit (eventType, ...params) {
    this.subs[eventType].forEach(handler => handler(...params))
  }
  • 注册时
function handleClick (params1) {
  console.log('clicked', params1)
}
em.$on('click', handleClick)

em.$on('click', (params1) => {
  console.log('clicked2', params1)
})
em.$on('change', (params1, params2) => {
  console.log(`changed:通过${params1},改变成${params2}`)
})
  • 触发时
em.$emit('click', '按钮1')
// > clicked
// > clicked2 按钮1

em.$emit('change', '按钮1', 999)
// > changed:通过按钮1,改变成999

完整代码

class EventEmitter {
  constructor() {
    this.subs = Object.create(null)
  }
  $on (eventType, handler) {
    this.subs[eventType] = this.subs[eventType] || []
    this.subs[eventType].push(handler)
  }
  $emit (eventType, ...params) {
    this.subs[eventType].forEach(handler => handler(...params))
  }
}

const em = new EventEmitter()

function handleClick (params1) {
  console.log('clicked', params1)
}
em.$on('click', handleClick)

em.$on('click', (params1) => {
  console.log('clicked2', params1)
})
em.$on('change', (params1, params2) => {
  console.log(`changed:通过${params1},改变成${params2}`)
})

em.$emit('click', '按钮1')
// > clicked
// > clicked2 按钮1

em.$emit('change', '按钮1', 999)
// > changed:通过按钮1,改变成999

todo

  • $off 销毁已经注册的事件

  • $once 注册一个只会触发一次的事件