是否可以将socket.io限制为每个事件只有一个侦听器?

时间:2017-06-15 14:54:08

标签: javascript sockets events websocket socket.io

我的问题是,是否可能限制特定事件的绑定侦听器可能性?例如,我有一个听众:

//In server
socket.emit('something', { message: 'Hello World!' });

//At client
socket.on('something', (data) => {
    console.log(data.message);  
});

//Output in console
Hello World!

但是如果我在代码中的其他位置复制该片段,我将2个相同的侦听器绑定到一个事件,因此我得到2个控制台日志,我只想要一个。

//In server
socket.emit('something', { message: 'Hello World!' });

//At client
socket.on('something', (data) => {
    console.log(data.message);  
});

socket.on('something', (data) => {
    console.log(data.message);  
});

//Output in console
Hello World!
Hello World!

我知道在单个事件上可能有多个侦听器用于不同的操作但是当我在某些框架中使用socket.io并在组件之间切换时(我在组件的构造函数中绑定),框架只是绑定它每次在组件之间切换时,都会监听同一事件。结果如上所述。

所以我的问题是:

  • 是否可以检查某个事件是否已有侦听器并忽略第二个绑定?
  • 或者,如果有一些socket.io配置设置允许每个事件只绑定一个侦听器,当你绑定一个新的侦听器时它将覆盖旧的一个?
  • 或者只是一些关于如何处理这种情况的好方法?

3 个答案:

答案 0 :(得分:2)

socket.io套接字派生自EventEmitter对象。因此,其侦听器功能完全来自该实现。 EventEmitter对象没有为某个给定事件阻止多个侦听器的功能。

所以,你有几个选择:

  1. 您可以删除第一个事件侦听器,只要您不再需要它。

  2. 您可以覆盖.on()以拒绝为已有处理程序的事件设置新处理程序,也可以在设置新处理程序之前删除先前的处理程序。

  3. 您可以更改逻辑,以便在您不再希望它激活时,您自己的代码会删除自己的事件侦听器。

  4. 这是一个你可以在每个新套接字上调用的函数,以避免为给定的eventName提供多个监听器:

    function hookSocketSingle(socket) {
        let origOn = socket.on;
        socket.on = function(eventName, listener) {
            // allow the event handler to be registered only if one doesn't already exist
            if (this.listenerCount(eventName) === 0) {
                return origOn.call(this, eventName, listener);
            } else {
                return this;
            }
        }
    }
    

答案 1 :(得分:1)

jfriend00的答案可以进一步简化b / c Emitter类具有一种称为hasListenershttps://socket.io/docs/client-api/

的方法。
  

套接字实际上继承了Emitter类的每个方法,例如   hasListeners,一次或关闭(以删除事件侦听器)。

let exist = socket.hasListeners(eventName)
if (exist) {
   // maybe just return
} else { 
   // the usual stuff
   socket.on(eventName, ...)
}

答案 2 :(得分:0)

在“paolo mania”评论和“jfriend00”的帮助下,我解决了Angular2框架和Socket.IO库的问题。

首先,我创建了一个服务,以便在整个组件创建和销毁周期中保持Socket.IO连接的开放。

import { Injectable } from '@angular/core';

declare var io: any;

@Injectable()
export class SocketService {

    // We create socket object for socket.io lib and events object 
    // to store all events that are bound from specific component 
    socket;
    events;

    // When the service is injected it checks if socket.io and our 
    // events objects are initialised
    constructor() {
        if(!this.socket) {
            this.socket = io(environment.apiURL);
            this.events = {};
        }
    }

    // Then we create a middleware function to intercept the socket.on()
    // function and we request one more parameter which is the component name.
    on(component, event, callback) {
        // We check if component key already exists in our events object. 
        if(this.events[component]) {
            // We check if the wanted event is already bound in selected component
            // (basically we check if its added to events[component] array) 
            if(this.events[component].indexOf(event) < 1) {
                // If it's not added we bind the event to socket and add it to events[component] array
                this.events[component].push(event);
                this.socket.on(event, callback);
            }
        // If the component key does not exist we initialise event[component] as an array 
        // and we add our event to the array as well as we bind the socket event
        } else {
            this.events[component] = [];
            this.events[component].push(event);
            this.socket.on(event, callback);
        }
    }

    // We also create a middleware function to intercept the socket.emit()
    // function and just forward data to socket.emit
    emit(event, data = {}) {
        this.socket.emit(event, data);
    }

    // And last we add unsubscribe function so we can unbind all listeners from
    // single component (mostly used in onNgDestroy() when we don't want that bound events
    // are persistent between more than one component)
    unsubscribe(component) {
        // We check if component key exists in events object
        if(this.events[component]) {
            // We iterate through array and we remove listeners for every 
            // event that was bound to selected component
            this.events[component].forEach((event) => {
                this.socket.off(event);
            });

            // And in the end we remove component key from events
            delete this.events[component];
        }
    }
}

所以我们有服务可以绑定我们想要的任意数量的事件,它们按组件排序,因此我们知道哪个组件具有哪些绑定。如果我们想要在同一个组件中绑定相同的事件,我们就会忽略它,当我们想要时,我们可以解除组件中的所有内容,这样当我们的组件被销毁或不活动时,不会触发一些有趣的操作。

在组件方面,我们像所有其他服务一样使用该服务:

import { ApiService } from '../core/socket.service'; 

@Component({
    templateUrl: './example.component.html',
    styleUrls: ['./example.component.scss']
})
export class ExampleComponent {

    // We inject our socket service in constructor
    constructor(private socket: SocketService) {
        // We bind wanted events with component name and handle them as we would normally in socket.io
        this.socket.on(this.constructor.name, 'event1', (data) => {
            console.log(data);
        });

        this.socket.on(this.constructor.name, 'event2', (data) => {
            console.log(data);
        });
    }

    ... other functions ...

     // If we don't want to keep our events persistent for another component we just unsubscribe.
     // When we will return to this component it will re-bind the events stated in component constructor.
     // But if we choose to use persistent mode it will not duplicate the events.
     ngOnDestroy() {
        this.socket.unsubscribe(this.constructor.name);
    }
}