【随手记】 Event Bus vs. Event Loop
参考:https://www.ruanyifeng.com/blog/2013/10/event_loop.html
https://blog.csdn.net/weixin_44759938/article/details/131401196
Event Bus(事件总线)
核心概念
- 角色:一个 中央化的消息分发系统,负责在不同组件/模块之间传递事件(消息)。
- 模式:基于 发布-订阅(Pub-Sub) 模型。
- 发布者(Publisher):触发事件。
- 订阅者(Subscriber):监听并处理事件。
- 特点:
- 多对多通信:一个事件可被多个订阅者处理。
- 解耦:发布者和订阅者无需知道彼此存在。
典型应用场景
- 前端框架(如 Vue 的
EventBus
)。 - 微服务架构中的跨服务通信(如 Kafka、RabbitMQ)。
- 组件化应用中的模块交互。
代码示例
// 创建一个事件总线
const eventBus = new EventBus();// 订阅事件
eventBus.subscribe('user_login', (data) => {console.log('用户登录了!', data);
});// 发布事件
eventBus.publish('user_login', { userId: 123 });
Event Loop(事件循环)
核心概念
- 角色:一种 任务调度机制,用于管理异步任务的执行顺序。
- 模式:基于 单线程循环检查任务队列。
- 主线程:执行同步代码。
- 任务队列:存放异步任务(如
setTimeout
、Promise
、I/O 操作)。
- 特点:
- 非阻塞:通过队列机制避免主线程阻塞。
- 顺序执行:按队列顺序处理任务(宏任务/微任务)。
典型应用场景
- JavaScript 运行时(如浏览器、Node.js)的异步执行。
- 高并发 I/O 操作(如网络请求、文件读写)。
运行流程(以浏览器为例)
- 执行同步代码(调用栈)。
- 异步任务完成后,回调函数进入任务队列。
- Event Loop 不断检查调用栈是否为空,若为空则从队列中取出任务执行。
┌───────────────────────┐│ Call Stack │└──────────┬────────────┘││┌──────────▼────────────┐│ Task Queue ││ (Macro/Micro Tasks) │└──────────┬────────────┘│┌──────────▼────────────┐│ Event Loop ││ (检查栈空后执行队列任务)│└───────────────────────┘
对比
对比维度 | Event Bus(事件总线) | Event Loop(事件循环) |
---|---|---|
本质 | 消息通信机制(跨组件/服务) | 任务调度机制(管理异步执行顺序) |
设计模式 | 发布-订阅(Pub-Sub) | 循环队列(Queue + Loop) |
核心角色 | 发布者(Publisher)、订阅者(Subscriber) | 调用栈(Call Stack)、任务队列(Task Queue) |
线程模型 | 通常多线程/跨进程(如微服务) | 单线程(如 JavaScript 主线程) |
典型应用场景 | - 前端组件通信(Vue EventBus) - 微服务消息队列(Kafka) | - JavaScript 异步任务 - Node.js I/O 操作 |
是否有序 | 事件可能无序到达(取决于实现) | 严格按队列顺序执行(宏任务/微任务优先级) |
耦合性 | 低耦合(发布者和订阅者无需相互感知) | 无直接耦合概念 |
代码示例 | eventBus.publish('event', data) | setTimeout(() => {}, 0) |
解决的问题 | 组件/服务间解耦通信 | 避免主线程阻塞,高效调度异步任务 |
底层依赖 | 可基于 Event Loop 实现(如浏览器中的自定义事件) | 操作系统/运行时原生支持(如 libuv 实现) |
- 都涉及“事件”:但 Event Bus 的“事件”是业务消息,Event Loop 的“事件”是异步任务。
- 均用于异步场景:但 Event Bus 解决的是 通信问题,Event Loop 解决的是 执行问题。
即:
- Event Bus 是 业务层概念,解决“谁通知谁”的问题(通信)。
- Event Loop 是 系统层概念,解决“何时执行”的问题(调度)。
两者可协同工作(例如:在前端中,Event Bus
的消息传递由 Event Loop
调度执行)。
案例
- Event Bus(解耦登录逻辑)
// 登录成功后发布事件
authService.login().then(() => {eventBus.publish('login_success');
});// 多个模块监听登录
eventBus.subscribe('login_success', () => {cartService.loadCart(); // 更新购物车
});eventBus.subscribe('login_success', () => {analytics.track('UserLogin'); // 发送埋点
});
- Event Loop(处理异步任务)
console.log('Start'); // 同步任务setTimeout(() => {console.log('Timeout'); // 宏任务
}, 0);Promise.resolve().then(() => {console.log('Promise'); // 微任务
});console.log('End'); // 同步任务// 输出顺序:Start → End → Promise → Timeout
// Event Loop 决定了微任务先于宏任务执行
- 用 Event Bus:当你需要 多个无关模块通信(如前端组件、微服务)。
- 用 Event Loop:当你需要 理解或优化异步代码的执行顺序(如避免 UI 阻塞)。