【问题标题】:How to unsubscribe from a socket.io subscription?如何取消订阅 socket.io 订阅?
【发布时间】:2012-03-14 04:36:03
【问题描述】:

假设有对象像这样订阅套接字服务器:

socket.on('news', obj.socketEvent)

这些对象的生命周期很短,并且经常被创建,从而产生许多订阅。这似乎是内存泄漏和容易出错的情况,可以通过这种方式直观地防止:

socket.off('news', obj.socketEvent)

在对象被删除之前,可惜的是,套接字中没有off 方法。有没有其他方法可以解决这个问题?

编辑:没有找到答案,我正在分配一个空白方法来覆盖原始事件处理程序的包装方法,下面是一个示例。

var _blank = function(){};

var cbProxy = function(){
    obj.socketEvent.apply(obj, arguments)
};
var cbProxyProxy = function(){
    cbProxy.apply ({}, arguments)
}
socket.on('news', cbProxyProxy);

// ...and to unsubscribe 
cbProxy = _blank;

【问题讨论】:

    标签: javascript socket.io publish-subscribe


    【解决方案1】:

    通过查看socket.io.js的源代码(在任何地方的文档中都找不到),我找到了这两个函数:

    removeListener = function(name, fn)
    removeAllListeners = function(name)
    

    我在我的应用程序中成功使用了removeAllListeners;您应该可以从以下选项中进行选择:

    socket.removeListener("news", cbProxy);
    socket.removeAllListeners("news");
    

    另外,我认为您的cbProxy = _blank 解决方案实际上不会起作用;这只会影响cbProxy 变量,不会影响任何实际的socket.io 事件。

    【讨论】:

    • 这似乎有效,事件从 SocketNamespace 中消失了,尽管我还没有对这部分进行广泛的测试。另外,您对代理解决方案的看法是正确的,因此为了幽默而对其进行了更新
    • 什么不起作用? cbProxy 是 OP 定义的回调方法。
    【解决方案2】:

    如果您想创建只“侦听”一次的侦听器,请使用socket.once('news',func)。 Socket.io 会在事件发生后自动销毁监听器——它被称为“volatile listener”。

    【讨论】:

      【解决方案3】:

      查看当前版本的 Socket.io Client (1.4.8) 的代码,似乎 offremoveAllListenersremoveEventListener 是都指向同一个函数。

      调用其中任何一个,提供事件名称和/或回调,都会得到所需的结果。根本不提供任何东西似乎会重置一切。

      请谨慎对待 fn/callback 参数。它必须与代码中使用的实例相同。

      例子:

      var eventCallback = function(data) {
        // do something nice
      };
      socket.off('eventName', eventCallback);
      

      会按预期工作。

      示例(也可以):

      function eventCallback(data) {
        // do something nice
      }
      socket.off('eventName', eventCallback);
      

      请注意,您尝试删除的回调是您传入的回调(这会带来很多混乱和挫败感)。 这个例子实现了一个初始回调的包装器,试图移除它不起作用,因为添加的真正回调是一个未公开的闭包实例:http://www.html5rocks.com/en/tutorials/frameworks/angular-websockets/

      这里是代码库中特定行的链接:https://github.com/socketio/socket.io-client/blob/master/socket.io.js#L1597

      【讨论】:

      • 这个答案需要更多的支持,因为大多数其他答案都不完整和/或非常过时。
      • 如果您使用的是socket.io-client npm 包,那么这是最好的,因为removeAllListeners() 是无参数的(因此您不能指定eventName)。
      • off 的回调都不会触发,如果给出回调,则 off 不会起作用。
      • 链接到文档here
      【解决方案4】:

      Socket.io 0.9.16 版实现了removeListener,但没有实现off

      退订时可以使用removeListener代替off,或者直接实现off如下:

        var socket = io.connect(url);
        socket.off = socket.removeListener;
      

      如果您使用 Backbone listenTo 事件订阅方法,则需要在取消订阅事件时将上述方法实现为 Backbone 调用 off

      【讨论】:

        【解决方案5】:

        我发现在 socket.io 0.9.11 和 Chrome24 socket.io removeListener 不起作用。

        这个修改后的版本适合我:

        EventEmitter.prototype.removeListener = function (name, fn) {
                if (this.$events && this.$events[name]) {
                    var list = this.$events[name];
        
                    if (io.util.isArray(list)) {
                        var pos = -1;
        
                        for (var i = 0, l = list.length; i < l; i++) {
                            if (list[i].toString() === fn.toString() || (list[i].listener && list[i].listener === fn)) {
                                pos = i;
                                break;
                            }
                        }
        
                        if (pos < 0) {
                            return this;
                        }
        
                        list.splice(pos, 1);
        
                        if (!list.length) {
                            delete this.$events[name];
                        }
                    } else  {
                            if (list.toString() === fn.toString() || (list.listener && list.listener === fn)) {
        
                               delete this.$events[name];
                            }
                    }
                }
        
                return this;
            };
        

        【讨论】:

          【解决方案6】:

          由于我在完成这项工作时遇到了一些麻烦,我想我也会在这里加入,以及 2017 年的一个很好的更新答案。感谢@Pjotr​​ 指出它必须是相同的回调实例。

          socket-io.subscriber 服务中的 Angular2 TypeScript 示例。注意“newCallback”包装器

            private subscriptions: Array<{
              key: string,
              callback: Function
            }>;
          
            constructor() {
              this.subscriptions = [];
            }
          
            subscribe(key: string, callback: Function) {
              let newCallback = (response) => callback(response);
              this.socket.on(key, newCallback);
              return this.subscriptions.push({key: key, callback: newCallback}) - 1;
            }
          
            unsubscribe(i: number) {
              this.socket.removeListener(this.subscriptions[i].key, this.subscriptions[i].callback);
            }
          

          【讨论】:

            【解决方案7】:

            在客户端移除事件监听器

            var Socket = io.connect();
            Socket.removeListener('test', test);
            

            【讨论】:

              【解决方案8】:

              使用数组预先存储事件,当您需要取消订阅它们时,使用 off 方法,这是 socket.io 的内置方法:

              // init
              var events = []
              
              // store
              events.push("eventName")
              // subscribe
              socket.on("eventName", cb)
              
              // remove
              events = events.filter(event => event!="eventName")
              // unsubscribe
              socket.off("eventName")
              

              【讨论】:

                【解决方案9】:

                添加到@Andrew Magee,这里有一个在 Angular JS 中取消订阅 socket.io 事件的示例,当然也适用于 Vanilla JS:

                function handleCarStarted ( data ) { // Do stuff }
                function handleCarStopped ( data ) { // Do stuff }
                

                监听事件:

                var io = $window.io(); // Probably put this in a factory, not controller instantiation
                io.on('car.started', handleCarStarted);
                io.on('car.stopped', handleCarStopped);
                
                
                $scope.$on('$destroy', function () {
                    io.removeListener('car.started', handleCarStarted);
                    io.removeListener('car.stopped', handleCarStopped);
                });
                

                【讨论】:

                  【解决方案10】:

                  同样在java 客户端上,也可以使用 Javascript 客户端以相同的方式完成。我从socket.io粘贴过来的。

                  // remove all listeners of the connect event
                  socket.off(Socket.EVENT_CONNECT);
                  
                  listener = new Emitter.Listener() { ... };
                  socket.on(Socket.EVENT_CONNECT, listener);
                  // remove the specified listener
                  socket.off(Socket.EVENT_CONNECT, listener);
                  

                  【讨论】:

                    【解决方案11】:

                    这对 Angular 8 和 React 16.8 都有帮助:

                    receiveMessage() {
                        let newCallback = (data) => {            
                            this.eventEmitter.emit('add-message-response', data);
                        };
                        this.socket.on('add-message-response', newCallback);
                    
                        this.subscriptions.push({key: 'add-message-response', callback: newCallback});
                    }
                    
                    receiveMessageRemoveSocketListener() {
                        this.findAndRemoveSocketEventListener('add-message-response');
                    }
                    
                    findAndRemoveSocketEventListener (eventKey) {
                        let foundListener = this.subscriptions.find( (subscription) => subscription.key === eventKey );
                    
                        if(!foundListener) {
                          return;
                        } 
                    
                        this.socket.removeListener(foundListener.key, foundListener.callback);
                        this.subscriptions = this.subscriptions.filter( (subscription) => subscription.key !== eventKey );
                    }
                    

                    使用订阅数组的原因是,当您多次订阅一个事件并且您没有从订阅列表中删除未订阅的订阅时,您很可能在第一次从列表中删除订阅时是正确的,但以后的订阅不会被删除,因为您只会在每次取消订阅事件时找到第一个实例。

                    你可以简单地调用receiveMessage();订阅事件并接收MessageRemoveSocketListener();退订。

                    【讨论】:

                      猜你喜欢
                      • 2022-01-19
                      • 1970-01-01
                      • 2015-08-24
                      • 2019-12-05
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2015-08-11
                      • 1970-01-01
                      相关资源
                      最近更新 更多