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

发布于 01-08 12:33 字数 621 浏览 5 评论 0原文

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

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

这些对象的生命周期很短,并且频繁创建,会生成许多订阅。这看起来像是内存泄漏和容易出错的情况,直观上可以通过这种方式来防止:

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

在删除对象之前使用 ,但是可惜的是,没有' t 套接字中的 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;

Suppose there are objects making subscriptions to a socket server like so:

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

These objects have a short life span and are frequently created, generating many subscriptions. This seems like a memory leak and an error prone situation which would intuitively be prevented this way:

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

before the object is deleted, but alas, there isn't an off method in the socket. Is there another method meant for this?

Edit: having found no answer I'm assigning a blank method to overwrite the wrapper method for the original event handler, an example follows.

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;

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

深者入戏2025-01-15 12:33:48

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

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

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

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

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

From looking at the source of socket.io.js (couldn't find it in documentation anywhere), I found these two functions:

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

I used removeAllListeners successfully in my app; you should be able to choose from these:

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

Also, I don't think your solution of cbProxy = _blank would actually work; that would only affect the cbProxy variable, not any actual socket.io event.

神妖2025-01-15 12:33:48

查看当前版本的 Socket.io 客户端(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

Looking at the code of current version of Socket.io Client (1.4.8) it seems that off, removeAllListeners, removeEventListener are all pointing to the same function.

Calling any of those, providing event name and/or callback, gives the desired result. Not providing anything at all seems to reset everything.

Please do be cautious about the fn/callback argument. It has to be the same instance used in the code.

Example:

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

Would work as expected.

Example (will also work):

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

Please be cautious that the callback you are trying to remove is the one that you passed in (this one can bring a lot of confusion and frustration).
This example implements a wrapper around initial callback, trying to remove that would not work as the real callback being added is an undisclosed closure instance: http://www.html5rocks.com/en/tutorials/frameworks/angular-websockets/

Here is the link to that specific line in the codebase: https://github.com/socketio/socket.io-client/blob/master/socket.io.js#L1597

兲鉂ぱ嘚淚2025-01-15 12:33:48

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

If you want to create listeners that "listens" only once use socket.once('news',func). Socket.io automatically will distroy the listener after the event happened - it's called "volatile listener".

灯下孤影2025-01-15 12:33:48

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

取消订阅时,您可以使用 removeListener 而不是 off,或者简单地实现 off,如下所示:

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

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

Socket.io version 0.9.16 implements removeListener but not off.

You can use removeListener instead of off when unsubscribing, or simply implement off as follows:

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

If you are using the Backbone listenTo event subscription approach, you'll need to implement the above as Backbone calls off when unsubscribing events.

碍人泪离人颜2025-01-15 12:33:48

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

I found that in socket.io 0.9.11 and Chrome24 socket.io removeListener doesn't work.

this modified version works for me:

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;
    };
云巢2025-01-15 12:33:48

由于我在做这项工作时遇到了一些麻烦,所以我也想在这里插话,并提供 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);
  }

Since I had a spot of troubles making this work figured I'd chime in here as well, along with a nice updated answer for 2017. Thanks to @Pjotr for pointing out that it has to be the same callback instance.

Example with Angular2 TypeScript in a socket-io.subscriber service. Note the "newCallback" wrapper

  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);
  }
娇柔作态2025-01-15 12:33:48

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

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

Removing an event listener on the client

var Socket = io.connect();
Socket.removeListener('test', test);
十年不长2025-01-15 12:33:48

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

Also on java client, it can be done the same way with the Javascript client. I've pasted from 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);
蹲在坟头点根烟2025-01-15 12:33:48

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

Pre-store the events using an array, and by the time you need to unsubscribe them, use the off method, which is a built in method from 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")
苍白女子2025-01-15 12:33:48

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

To add to @Andrew Magee, here is an example of unsubscribing socket.io events in Angular JS, and of course works with Vanilla JS:

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

Listen for events:

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);
});
霓裳挽歌倾城醉2025-01-15 12:33:48

这对我在 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();订阅事件并 receiveMessageRemoveSocketListener();取消订阅。

This has helped me in both Angular 8 and 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 );
}

Reason for using an Array of Subscriptions is that when you Subscribe to an event multiple times and you don't remove an unsubscribed subscription from the Subscription list you will most probably be right at first time you remove the subscription from the list, but later subscriptions will not be removed as you will be finding first instance only every time you unsubscribe the event.

You can simply call receiveMessage(); to subscribe to an the event and receiveMessageRemoveSocketListener(); to Unsubscribe.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文