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

Posted

技术标签:

【中文标题】是否可以将 socket.io 限制为每个事件只有一个侦听器?【英文标题】:Is it possible to limit socket.io to have only one listener per event? 【发布时间】:2017-11-18 03:16:03 【问题描述】:

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

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

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

//Output in console
Hello World!

但是,如果我在代码中的其他位置复制该 sn-p,我会将 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 配置设置允许每个事件只绑定一个侦听器,并且当您绑定一个新的侦听器时,它会覆盖旧的侦听器? 或者只是一些关于如何处理这种情况的良好做法?

【问题讨论】:

当你改变组件时停止监听,使用:socket.removeListener('something', something);更多信息:***.com/questions/23092624/… 谢谢,我已经在 angular2 中编写了一个类似 service 的接口,并将其发布在这里作为答案,我将您的介词与 socket.off 函数一起使用(据我所知,这与 socket.removeListener 相同) ) 【参考方案1】:

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

所以,你有几个选择:

    当您不再希望附加第一个事件侦听器时,您可以删除它。

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

    您可以更改逻辑,以便您自己的代码在您不再希望它处于活动状态时删除它自己的事件侦听器。

这是一个您可以在每个新套接字上调用的函数,以避免给定的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;
        
    

【讨论】:

【参考方案2】:

jfriend00的回答可以进一步简化b/c Emitter类有一个方法叫hasListeners,https://socket.io/docs/client-api/

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

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

【讨论】:

【参考方案3】:

在“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);
    

【讨论】:

是否应该从您提出的问题中得到所有这些信息?似乎我的回答回答了您提出的问题,并且您在此处显示的很多内容与提出的实际问题无关,而是建立在我在回答中解释的概念之上。我知道这是您试图解决的全部问题,但其中大部分似乎与您提出的问题没有任何关系。有点惊讶,我猜这是公认的答案。 当您从他们的答案中获取概念,将其与许多与您在问题中提出的问题无关的内容结合起来,然后接受您自己的答案时,这并不真正鼓励人们提供答案。跨度> 对不起,我已将接受的答案更改为您的答案,但是因为我想分享我的最终结论,它确实解决了我的问题:D(确实使用了您的概念)但是当我问问题我尽量抽象,因为更多人可以回答。所以很难不得到抽象问题的概念性答案。真诚的 实际上这里的抽象问题通常不如您的真实代码中的具体问题有用。我可能已经发布了 500 次,要求人们摆脱抽象的构成代码并发布他们的真实代码,以便我们可以帮助解决真正的问题。无论如何,很高兴你有一个解决方案。问一个关于特定解决方案的抽象问题假定您知道最佳解决方案类型。发布真正的问题(不仅仅是您的解决方案的问题)可以让人们提出您甚至没有想到的解决方案。

以上是关于是否可以将 socket.io 限制为每个事件只有一个侦听器?的主要内容,如果未能解决你的问题,请参考以下文章

Socket.io 睡眠一些客户端

处理浏览器重新加载 socket.io

Web Socket & Socket.io

Socket.io 命名空间限制以及如何扩展它

如何将 socket.io-client 导入每个组件? [复制]

socket.io 中最大同时打开的房间