You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 02:15:34