事件总线核心是将异步监听器执行权交还调度方,支持并行(Promise.all)、串行(for...of+await)及混合策略,通过.catch或safeCall实现错误隔离,内置AbortSignal与timeout兜底,并用TypeScript泛型、WeakMap、once/destroy保障类型安全与生命周期可控。

核心不是让事件总线“变异步”,而是把异步监听器的执行控制权交还给调度方——你决定等不等、怎么等、等多久、出错后如何处理。
监听器自然返回 Promise,触发时按需编排执行流
注册监听器时无需特殊封装,async 函数或返回 Promise 的普通函数均可直接使用:
- 并行执行(多数场景):用 Promise.all 并发触发所有监听器,适合无依赖关系的副作用,如日志上报、埋点、缓存更新
- 串行执行(状态强依赖):用 for...of + await 逐个等待,比如“用户登录 → 拉取权限 → 初始化菜单”这类链式流程
- 混合策略:对某类事件分组并发,组间串行,兼顾效率与顺序保障
单点错误隔离,避免雪崩式中断
一个监听器 reject 不应导致整个事件流失败。关键做法是不直接 await 原始回调,而是包装为安全调用:
- 每个监听器调用包裹 .catch(() => {}),静默吞掉非关键错误
- 或统一用 safeCall(fn, args) 工具函数:内部 try/catch + 可选错误日志 + 返回 void Promise
- 允许监听器显式抛出特定错误类型(如 AbortError),由总线识别后跳过后续监听器
内置取消信号与超时兜底
真实业务中常需主动终止未完成的监听链,例如页面卸载、表单取消、用户退出等:
- emit 支持传入 AbortSignal:每次调用监听器前检查
signal.aborted,已中止则跳过 - 支持 timeout 选项:对每个监听器用
Promise.race([fn(), timeoutPromise])包裹,超时后 resolve 空值或 reject 特定 TimeoutError - 不强制监听器实现取消逻辑,但提供 signal 透传能力,让有需要的监听器可自行响应
类型安全 + 生命周期可控
长期运行的系统必须防内存泄漏,同时保障类型不越界:
- 用 TypeScript 泛型约束事件名与参数,例如
type Events = { 'auth:login': [User]; 'cart:change': [Cart] },保证bus.emit('auth:login', user)编译期校验 - off() 必须精确匹配监听器引用,推荐用 WeakMap 存储 listener → handler 映射,避免闭包强持有
- 提供 once() 和 destroy():前者自动在首次触发后清理;后者批量清空某模块注册的所有监听器,适合组件 unmount 场景


















