前端面试 P040:发布订阅、事件总线与退订
目前顯示中文(中國)原文
来源:方格前端讲面试 · 40.实现一个发布订阅模式,12 分 03 秒。
回看定位:03:06 模式 · 06:15 on · 08:43 退订 · 10:45 批处理
发布者与订阅者通过消息中心协作
on 注册某个事件的处理器,emit 通知,off 或返回的清理函数解除注册。消息总线减少直接组件引用,但双方仍依赖事件名、载荷结构、时序等契约,不是“彻底没有耦合”。
class EventBus {
#events = new Map();
on(event, handler) {
if (typeof handler !== 'function') throw new TypeError('Expected handler');
let listeners = this.#events.get(event);
if (!listeners) this.#events.set(event, listeners = new Set());
listeners.add(handler);
return () => this.off(event, handler);
}
off(event, handler) {
const listeners = this.#events.get(event);
listeners?.delete(handler);
if (listeners?.size === 0) this.#events.delete(event);
}
emit(event, ...args) {
for (const handler of [...(this.#events.get(event) ?? [])]) handler(...args);
}
}
const bus = new EventBus();
const stop = bus.on('update', value => console.log(value));
bus.emit('update', 1); // 1
stop();
bus.emit('update', 2); // 无输出
本版本相同函数在同一事件下去重,emit 同步执行快照;某个处理器抛错会中断当前发布。快照意味着发布过程中退订的其他回调仍可能在本轮执行。这些都属于应明确的接口选择。
生命周期与状态
组件卸载时退订,避免全局总线长期保留闭包。普通总线不保存历史事件,先 emit 后订阅通常收不到;购物车数量这种持久共享状态也许更适合状态容器,而不是只传瞬时事件。
需要 once 时,应先移除包装监听再执行目标,避免回调重入造成再次触发。异步处理器返回的拒绝,本同步总线不会自动等待或聚合,也应设计边界。
批处理不是自动避免卡顿
视频建议 requestIdleCallback 后一次性执行队列的全部任务,仍可能超出空闲预算、阻塞 UI。应限制批次、检查剩余预算并安排续批;需明确延后会改变同步通知语义,不能直接当作无差别优化。