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

EventSource调用removeEventListener无法移除绑定事件问题

问题原因
  • 单一handler引用被覆盖:闭包内仅声明了一个全局handle变量,每次调用addEventListener都会用新的函数引用覆盖该变量,之前绑定事件时使用的handler引用会直接丢失。调用removeEventListener时传入的是最后一次赋值的handler,无法匹配此前绑定的事件回调,导致移除失败。
  • 缺少事件映射关系:没有存储「事件名-原回调函数-包装后handler」的对应关系,既不支持同一事件绑定多个回调,也无法精准匹配要移除的回调。
  • 状态判断逻辑存在隐患:es初始化后会被赋值为EventSource实例,本身不存在sseinit属性,判断条件容错性极低,未初始化时调用移除方法容易触发异常。
修复方案

修正后的封装代码如下:

var sse = {};
(function(s){
    // 单独维护初始化状态,不绑定到es对象
    let isSseInit = false;
    let es = null;
    // 存储事件映射:key为事件名,value为[{originalCallback, wrappedHandler}]
    const eventMap = {};

    s.init = function (url) {
        if (isSseInit) return;
        if (typeof EventSource === 'function') {
            es = new EventSource(url);
            isSseInit = true;
        } else {
            es = {};
            console.warn('当前环境不支持EventSource');
        }
    };

    s.close = function () {
        if (!isSseInit || !es) return;
        es.close();
        // 清空所有绑定的事件
        Object.keys(eventMap).forEach(eventName => {
            eventMap[eventName].forEach(({ wrappedHandler }) => {
                es.removeEventListener(eventName, wrappedHandler);
            });
            delete eventMap[eventName];
        });
        es = null;
        isSseInit = false;
    };

    s.addEventListener = function (name, callback) {
        if (!isSseInit || !es) return;
        const wrappedHandler = function(e) {
            callback(e, JSON.parse(e.data));
        };
        if (!eventMap[name]) eventMap[name] = [];
        eventMap[name].push({
            originalCallback: callback,
            wrappedHandler: wrappedHandler
        });
        es.addEventListener(name, wrappedHandler);
        console.log('addEventListener :: '+name);
    };

    s.removeEventListener = function (name, callback) {
        if (!isSseInit || !es || !eventMap[name]) return;
        // 匹配对应回调的包装handler
        const targetIndex = eventMap[name].findIndex(item => item.originalCallback === callback);
        if (targetIndex === -1) return;
        const { wrappedHandler } = eventMap[name].splice(targetIndex, 1)[0];
        es.removeEventListener(name, wrappedHandler);
        if (eventMap[name].length === 0) delete eventMap[name];
        console.log('removeEventListener :: '+name);
    };
}(sse));
使用注意事项
  • 调用removeEventListener时,必须传入和绑定时完全相同的回调函数引用,不要传入匿名函数,否则无法匹配移除。
  • 支持同一事件绑定多个不同回调,各自可独立移除,互不影响。
  • 修复了原代码中EventSource兼容性判断的逻辑错误,避免异常触发。

内容的提问来源于stack exchange,提问作者Aheuh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 19:48:01