VueJS 中 event bus 的实现

最近在工作中,使用了消息总线(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 的实现》有一个想法

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据