为什么这个简单的基于数组的事件发射器顺序敏感?
Why is this simple array based event emitter order sensitive?
如果事件处理程序前面有“.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' 处理程序移到其他两个处理程序下方,一切都会顺利触发,这是为什么?
这是因为 (listeners[event] || []).forEach(h => h(data))
将使用从 0
开始的索引器并为每个项目递增它。当您删除一个项目时(调用 once
调用 off
时)索引是错误的,实际上会跳过行中的下一个项目。
最简单的补救方法是复制事件:
(listeners[event] || []).slice().forEach(h => h(data));
如果事件处理程序前面有“.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' 处理程序移到其他两个处理程序下方,一切都会顺利触发,这是为什么?
这是因为 (listeners[event] || []).forEach(h => h(data))
将使用从 0
开始的索引器并为每个项目递增它。当您删除一个项目时(调用 once
调用 off
时)索引是错误的,实际上会跳过行中的下一个项目。
最简单的补救方法是复制事件:
(listeners[event] || []).slice().forEach(h => h(data));