Как отписаться от подписки на socket.io?

Предположим, что есть объекты, делающие подписки на сервер сокетов, например:

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;

person Bijou Trouvaille    schedule 23.02.2012    source источник


Ответы (11)


Посмотрев на источник socket.io.js (нигде не смог найти его в документации), я нашел эти две функции:

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

Я успешно использовал removeAllListeners в своем приложении; вы должны иметь возможность выбирать из них:

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

Кроме того, я не думаю, что ваше решение cbProxy = _blank действительно сработает; это повлияет только на переменную cbProxy, а не на какое-либо фактическое событие socket.io.

person Andrew Magee    schedule 14.03.2012
comment
Кажется, это работает, событие исчезает из SocketNamespace, хотя мне еще предстоит провести обширное тестирование этой части. Также вы были правы насчет прокси-решения, поэтому оно обновлено для юмора. - person Bijou Trouvaille; 14.03.2012
comment
Что не работает? cbProxy был методом обратного вызова, определенным OP. - person Andrew Magee; 11.08.2015

Если вы хотите создать слушателей, которые «слушают» только один раз, используйте socket.once('news',func). Socket.io автоматически уничтожит прослушиватель после того, как событие произошло — это называется «изменчивый прослушиватель».

person András Endre    schedule 09.08.2013

Глядя на код текущей версии клиента Socket.io (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

person Pjotr    schedule 15.08.2016
comment
Этот ответ требует большего количества голосов, так как большинство других являются неполными и / или крайне устаревшими. - person Anson Kao; 29.10.2016
comment
Если вы используете пакет socket.io-client npm, то это работает лучше всего, так как removeAllListeners() не имеет параметров (поэтому вы не можете указать eventName). - person Martin Schneider; 02.04.2018
comment
Ни обратный вызов off не срабатывает, ни off не работает, если задан обратный вызов. - person Koushik Shom Choudhury; 09.06.2020
comment
Ссылка на документы здесь. - person ViniciusArruda; 04.03.2021

Socket.io версии 0.9.16 реализует removeListener, но не off.

Вы можете использовать removeListener вместо off при отписке или просто реализовать off следующим образом:

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

Если вы используете подход подписки на события Backbone listenTo, вам потребуется реализовать описанное выше, так как Backbone вызывает off при отказе от подписки на события.

person deedw    schedule 15.10.2013

Я обнаружил, что в 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;
    };
person radiofrequency    schedule 04.02.2013

Поскольку у меня были некоторые проблемы с выполнением этой работы, я решил, что тоже буду здесь, вместе с хорошим обновленным ответом на 2017 год. Спасибо @Pjotr ​​за то, что он указал, что это должен быть тот же экземпляр обратного вызова.

Пример с Angular2 TypeScript в сервисе socket-io.subscriber. Обратите внимание на оболочку «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);
  }
person Joshua Ohana    schedule 08.03.2017

Удаление прослушивателя событий на клиенте

var Socket = io.connect();
Socket.removeListener('test', test);
person Jijo Paulose    schedule 29.12.2015

Предварительно сохраните события, используя массив, и к тому времени, когда вам нужно будет отписаться от них, используйте метод 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")
person Raz    schedule 07.04.2019

Чтобы добавить к @Andrew Magee, вот пример отказа от подписки на события socket.io в Angular JS, и, конечно же, работает с 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);
});
person loonison101    schedule 04.08.2016

Также на 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);
person efkan    schedule 08.08.2016

Это помогло мне как в 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(); Отписаться.

person Tofiq Quadri    schedule 11.09.2019