基于DOM的JavaScript Pub/Sub代码unsubscribe失效问题求助
问题原因
你的subscribe函数实际注册的是匿名包装函数,而非你传入的原始callback:
pubsub.addEventListener(eventName, (e, data) => { callback(e.detail); });
removeEventListener要求必须传入和addEventListener完全相同的函数引用才能生效,但你调用unsubscribe时传入的setTargetFn,和注册的匿名包装函数是两个完全独立的函数引用,自然无法匹配,导致取消订阅失败。
解决方案
有两种可行的修改方式:
方式1:调整subscribe逻辑,直接传递原始回调(需修改业务代码参数接收)
让subscribe直接注册原始回调,业务回调自行处理事件对象:
export const subscribe = (eventName, callback) => { pubsub.addEventListener(eventName, callback); }; export const unsubscribe = (eventName, callback) => { pubsub.removeEventListener(eventName, callback); }; // 业务代码调整回调,接收event参数并获取detail const setTargetFn = (event) => { unsubscribe("setTargetUnitId", setTargetFn); createMessage("Great job! Now attack it by pressing 1 and going near it", 0); // 如需使用数据可通过 event.detail 获取 }; subscribe("setTargetUnitId", setTargetFn);
方式2:维护包装函数映射表(无需修改业务代码)
在Pub/Sub模块内维护一个映射表,记录每个事件下原始回调对应的包装函数,确保unsubscribe能找到正确的函数引用:
// 新增映射表,存储事件名->原始回调->包装函数的对应关系 const callbackMap = new Map(); export const subscribe = (eventName, callback) => { const wrapper = (e) => { callback(e.detail); }; // 初始化事件对应的回调映射 if (!callbackMap.has(eventName)) { callbackMap.set(eventName, new Map()); } callbackMap.get(eventName).set(callback, wrapper); // 注册包装函数 pubsub.addEventListener(eventName, wrapper); }; export const unsubscribe = (eventName, callback) => { if (!callbackMap.has(eventName)) return; const eventCallbacks = callbackMap.get(eventName); const wrapper = eventCallbacks.get(callback); if (wrapper) { pubsub.removeEventListener(eventName, wrapper); eventCallbacks.delete(callback); // 清理空事件的映射,避免内存占用 if (eventCallbacks.size === 0) { callbackMap.delete(eventName); } } }; export const publish = (eventName, data) => { pubsub.dispatchEvent(new CustomEvent(eventName, { detail: data })); };
这种方式不需要改动你的业务调用代码,setTargetFn可以保持原有写法,取消订阅功能会正常生效。
验证说明
修改完成后,调用unsubscribe时会精准匹配到addEventListener注册的包装函数,从而正确移除订阅,避免后续事件触发时重复执行回调。
内容的提问来源于stack exchange,提问作者bezzoon
相关产品推荐
相关产品推荐

