最近在工作中,使用了消息总线(message bus)这么一个东西,想起 Vue 也有类似的东西,所以想看一下其源码实现。
学习英语是理解编程的第一步。bus 有公共汽车的意思,也有 ‘a set of wires that carries information from one part of a computer system to another’(计算机系统的总线)。所以这里 bus 的意思是总线,一种形象的说法,为了更进一步的了解这个概念,可能需要一点硬件知识。
完整代码
2021-04-03 更新
简单的 bus 可以理解为把事件的订阅函数放在一个对象里,如:
let bus = {}
function on(topic, fn) {
if (!bus[topic]) bus[topic] = [fn]
else bus[topic].push(fn)
}
function emit(topic, ...args) {
let res = []
for (let fn of bus[topic]) res.push(fn.apply(null, args))
return res
}
let topic = 'click'
on(topic, console.log)
on(topic, (...args) => args)
let res = emit(topic, 1, 2, 3)
console.log(res)
上面的代码有一个全局的 bus
对象,key
是每个事件名,值是函数数组。函数 on
用来订阅函数,emit
用来发布事件,拿到订阅函数执行的返回结果,这和 Vue 中的 event bus 并无二致。但是我们要解决一个更难一点的问题:怎么通过 emit
发布一个异步事件,并且拿到等待执行这个异步事件后返回的结果。
闲话少叙,看代码:
let data = {}
function register(topic, fn) {
on(topic, (evidence) => {
let { uid, args } = evidence
let p = fn.apply(null, args)
p.then(res => {
data[topic + uid] = res
emit(topic + uid)
delete data[top + uid]
})
})
}
function fetchAsync(topic, ...args) {
return new Promise(resolve => {
emit(topic, { uid, args })
let str = topic + uid
on(str, () => {
resolve(data[str])
})
uid++
})
}
let uid = 0
let topic2 = 'async'
register(topic2, (a) => {
return new Promise(resolve => {
setTimeout(() => {
resolve(a === 1 ? 'hello' : 'world')
}, 1000);
})
})
fetchAsync(topic2, 2).then(v => {
console.log(v) // world
})
fetchAsync(topic2, 1).then(v => {
console.log(v) // hello
})
总结
bus 的实现是大同小异的,好像也没什么特别要说的,满足一下轻微的好奇心。想了解实现的细节,可以查看例子的详细代码。
《VueJS 中 event bus 的实现》有一个想法