为什么这个基于简单数组的事件发射器顺序敏感?

问题描述 投票:1回答:1

如果事件处理程序前面有'.once'事件处理程序,则它不会触发事件处理程序。无法理解为什么会这样。

const emitter = (host = {}, listeners = {}) => Object.assign(host, {
  emit (event, data) {
    (listeners[event] || []).forEach(h => h(data))
  },
  on (event, handler) {
    if (!listeners[event]) listeners[event] = []
    listeners[event].push(handler)
    return () => host.off(event, handler)
  },
  once (event, handler) {
    if (!listeners[event]) listeners[event] = []
    listeners[event].push(function h () {
      handler(...arguments)
      host.off(event, h)
    })
  },
  off (event, handler) {
    const i = (listeners[event] || []).findIndex(h => h === handler)
    if (i > -1) {
      listeners[event].splice(i, 1)
      if (!listeners[event].length) delete listeners[event]
    }
  }
})

// EXAMPLE

const e = emitter()

e.once('msg', msg => {
    console.log('once.msg: ', msg)
})

e.on('msg', msg => { // <- not firing
    console.log('on.msg: ', msg)
})

e.on('msg', msg => {
    console.log('on_1.msg: ', msg)
})

e.emit('msg' ,'See me?')

第一个'on.msg'处理程序根本不会触发,但是如果'once.msg'处理程序移动到另外两个处理程序之下,所有内容都会顺利触发,为什么会这样?

javascript arrays push eventemitter
1个回答
2
投票

这是因为(listeners[event] || []).forEach(h => h(data))将使用从0开始的索引器并为每个项目递增它。当你删除一个项目时(调用调用onceoff时)索引是错误的,实际上会跳过下一个项目。

解决这个问题的最简单方法是复制事件:

(listeners[event] || []).slice().forEach(h => h(data));
© www.soinside.com 2019 - 2024. All rights reserved.