如何让EventEmitter监听Node.js中的另一个EventEmitter?

时间:2014-10-20 12:07:49

标签: javascript node.js events publish-subscribe eventemitter

我想做这样的事情:

var events = require("events");

var emitterA = new events.EventEmitter();
var emitterB = new events.EventEmitter();

emitterA.addListener("testA", function(){
    console.log("emitterA detected testA");
});

emitterB.addListener("testA", function(){
    console.log("emitterB detected testA");
});

emitterA.emit("testA");

输出是这样的:

emitterA detected testA
emitterB detected testA

但是当我运行这段代码时,我得到的输出是:

emitterA detected testA

我基本上想要一个发射器来侦听另一个发射器发出的事件。

理由: 我在Node上编写服务器,服务器发送Server Sent Events。这意味着我需要持久连接。为了确保连接由于某种原因没有关闭(我不想将超时设置为无限,因为这感觉就像一个黑客并且看起来并不安全),我发出的心跳只是空白数据,只是将某些内容传递给浏览器。

我正在运行一个计时器,并且每个时间段(在我的测试用例中为1秒),它将触发server.emit("hb");之后我会同时写入并向所有请求对象发送数据(在我的笔记本电脑上,这只是多个标签和多个浏览器)。基本上req.on("hb", callback)

对我来说,这看起来更干净,因为替代方案是为每个请求对象提供自己的计时器,这意味着将会有许多计时器在各处运行,每个计时器都会导致其各自的请求对象发出心跳事件。这似乎是一种非理想的做事方式。

另外,因为每个请求对象都是自发创建和销毁的,所以这样做基本上可以确保创建和销毁监听器。

所以我想要的是服务器发出的心跳事件,所有活动的请求对象都会听到它并沿着自己的连接写入数据。

另一个替代方案(我现在正在使用的方法)是让服务器监听自己的事件,并让服务器编写心跳。这个问题是服务器上的maxListeners限制 - 每个请求对象都会追加一个新的" hb"侦听器到服务器,以便服务器可以侦听该特定请求对象的事件。然而,最大的听众是10,虽然我也可以将其设置为无限,但我并不太热衷于这样做,因为我真的很好奇是否有更好的方法。

对我来说,最好和最干净的解决方案似乎是制作请求对象"订阅"到服务器事件。我这样做是为了学习如何在节点中编程,所以我想尽可能少地实现外部库并使用节点的原始功能来实现,但是如果需要外部库,我很乐意阅读其源代码,以便我可以学习如何实现一个小的本地实现。

2 个答案:

答案 0 :(得分:3)

请注意,EventEmitters是javascript中的唯一来源。它们不是每个人都能听到的全系统事件。它们是可以发出事件以支持异步模式的对象。要完成你想要的,你需要多个东西来听同一个发射器。像这样:

'use strict';
var events = require( 'events' );
//Create a new sole-source event emitter
var emitterA = new events.EventEmitter();

//create a container that can listen
function EventListener( name ) {
  console.log( 'new event listener, name=' + name );
  this.name = name;
  this.ack = function() {
    console.log( this.name + ' just heard testA' );
  };
  this.listenTo = function( event, emitter ) {
    var self = this;
    emitter.on( event, function() {
      self.ack();
    } );
  };
}

var listenerA = new EventListener( 'A', emitterA );
listenerA.listenTo( 'testA', emitterA );

var listenerB = new EventListener( 'B', emitterA );
listenerB.listenTo( 'testA', emitterA );

setInterval( function() {
  emitterA.emit( 'testA' );
}, 1000 );

输出:

$ node testfile.js 
new event listener, name=A
new event listener, name=B
A just heard testA
B just heard testA
A just heard testA
B just heard testA
...

请注意,对于其他用例,您将需要使用WebSockets。 EventEmitters将无法以您希望的方式工作,尤其是远程客户端。 (socket.io的评论确实是一个很好的起点。)

答案 1 :(得分:0)

您可以尝试将事件向前发送到元素emitterB

emitterA.addListener("testA", function(){
    console.log("emitterA detected testA");

    // When everything is done emit the event to emitterB
    emitterB.emit('testA');
});

或者,如果要向前发射多个事件,您可以创建类似事件冒泡的内容:

function bubbleEvents(from, to) {

    var oldEmit = from.emit;

    from.emit = function (event) {

        var args = Array.prototype.slice.call(arguments, 1),
            newArgs = [event].concat(args);

        oldEmit.apply(from, newArgs);
        to.emit.apply(to, newArgs);
    };
}

// and call it
bubbleEvents(emitterA, emitterB);

P.S。编辑像这样的对象的方法不是一个好习惯,但是出于某些肮脏的目的,它可以很好地服务。