如何取消订阅socket.io订阅?

时间:2012-02-23 18:10:50

标签: javascript socket.io publish-subscribe

假设存在对套接字服务器进行订阅的对象,如下所示:

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;

11 个答案:

答案 0 :(得分:74)

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

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

我在我的应用中成功使用了removeAllListeners;你应该可以从这些中选择:

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

另外,我不认为你cbProxy = _blank的解决方案确实有效;这只会影响cbProxy变量,而不会影响任何实际的socket.io事件。

答案 1 :(得分:29)

如果要创建仅使用socket.once('news',func)“侦听”一次的侦听器。事件发生后,Socket.io会自动破坏侦听器 - 它被称为“volatile listener”。

答案 2 :(得分:20)

查看当前版本的Socket.io Client(1.4.8)的代码,似乎 off removeAllListeners removeEventListener 是都指向同一个功能。

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

请注意 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

答案 3 :(得分:5)

Socket.io版本0.9.16实现removeListener但不实现off

取消订阅时,您可以使用removeListener代替off,或按以下方式简单实施off

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

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

答案 4 :(得分:3)

我发现在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;
    };

答案 5 :(得分:1)

删除客户端上的事件侦听器

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

答案 6 :(得分:1)

由于我有一些麻烦使这项工作变得很好,我也会在这里发出响应,以及2017年的一个很好的更新答案。感谢@Pjotr​​指出它必须是相同的回调实例

socket-io.subscriber服务中的Angular2 TypeScript示例。注意&#34; newCallback&#34;包装

  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 :(得分:0)

要添加到@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);
});

答案 8 :(得分:0)

同样在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);

答案 9 :(得分:0)

使用数组预先存储事件,在需要取消订阅时,请使用 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")

答案 10 :(得分:0)

这对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();取消订阅。