如何在Node.js中使用JavaScript模块文件之间的事件处理程序?

17

我目前正在使用 socket.io 在客户端JavaScript文件和Node.js服务器文件之间进行事件的发送和接收,但我希望能够在Node服务器及其模块之间进行事件的发送和接收。我的想法是它可能看起来像这样:

Node 服务器:

var module1 = require('./module1');

//Some code to launch and run the server

module1.emit('eventToModule');
module1.emit('moduleResponse', function(moduleVariable) {
   //server action based on module response
}

模块文件:

var server = require('./server.js');

server.on('eventToModule', function() {
   //module response to server request
}
server.emit('moduleResponse', moduleVariable);

这显然是一个简化版本,但我认为应该有这个功能。我需要将模块文件设置为第二个服务器吗?如果是这样的话,它会是什么样子?

我还尝试过使用 var socket = io.connect('http://localhost:3000'); (这是我用来允许客户端连接到Node服务器的代码)代替 server 并且让 module1 监听和发出 socket 但那也没用。

第二次尝试(仍然不起作用):

server.js

//other requirements
var module1 = require('./module');
const EventEmitter = require('events');
var emitter = new EventEmitter();

io.on('connection', function(client) {
   client.on('emitterTester', function() {
      emitter.emit('toModule');
      emitter.on('toServer', function() {
         console.log("Emitter successful.");
      });
   });
});

module.exports = emitter;

模块.js

var server1 = require('./server');
const EventEmitter = require('events');
var emitter = new EventEmitter();

emitter.on('toModule', function() {
   console.log("Emitter heard by module.");
   emitter.emit('toServer');         
});

module.exports = emitter;

同时,当我尝试使用server1.on时,我收到了消息server1.on不是一个函数

3个回答

35
在node.js中,如果你想创建一个拥有事件监听器并能触发事件的对象,通常会使用EventEmitter对象。你可以直接使用EventEmitter对象,也可以从它继承并创建自己的对象,拥有所有EventEmitter功能。
因此,如果你想创建一个其他模块可以侦听事件的模块,你需要像这样操作:
// module1.js
// module that has events

// create EventEmitter object
var obj = new EventEmitter();

// export the EventEmitter object so others can use it
module.exports = obj;

// other code in the module that does something to trigger events
// this is just one example using a timer
setInterval(function() {
    obj.emit("someEvent", someData);
}, 10 * 1000);

然后,您可以有另一个模块使用第一个模块并监听从其传出的某些事件:

// module2.js
var m1 = require('module1.js');

// register event listener
m1.on("someEvent", function(data) {
    // process data when someEvent occurs
});
这里的关键点是:
  1. 如果你想让一个模块允许人们监听事件并触发事件,你可能想创建一个EventEmitter对象。
  2. 要共享该 EventEmitter 对象,你将其分配给module.exportsmodule.exports的属性,以便其他代码可以通过使用require()来访问EventEmitter对象。
  3. 一旦调用代码从require()获取了EventEmitter对象,它就可以使用.on()方法注册以侦听事件。
  4. 当原始模块或任何模块想要触发事件时,可以使用.emit()方法。

请记住,有时事件是很好的架构选择,但不是所有模块之间的通信都适合于事件。有时,只需导出函数并允许一个模块调用另一个模块的函数可能会更合理。因此,事件并不是模块之间通信的唯一方式。


你的问题似乎表明你认为socket.io是两个同一服务器进程中的模块进行通信的一种方式。虽然这可能是可能的,但这不是socket.io通常被使用的方式。通常,基于TCP/IP的socket.io会被用于在两个分离的进程之间进行通信,这样您就无法在进程内直接进行函数调用或注册事件处理程序。后两种方案通常更容易实现进程内通信,而socket.io通常用于在同一计算机上的进程之间或在不同计算机上的进程之间进行通信。


@SuperCodeBrah - 你没有展示足够的代码(我们需要在server.js中看到module.exports),但是看起来你没有从server.js导出EventEmitter,然后在module.js中使用该特定的eventEmitter实例。请参考我的代码示例如何做到这一点。 - jfriend00
是的,在第一次编辑中我错过了那个。现在应该没问题了。此外,事件在服务器和模块之间是双向传递的,所以我认为无论我在模块中有什么,我都需要在服务器上以及反过来。 - SuperCodeBrah
1
@SuperCodeBrah - 你可以使用一个事件发射器,让每一方都向其发射并监听不同的事件,或者每一方都可以导出自己的发射器 - 这取决于具体情况。如果这回答了你的问题,你可以在回答左侧勾选绿色的复选标记,以示告知社区并因遵循正确程序而获得声望。 - jfriend00
1
你没有在正确的发射器上调用.emit()。由于你创建了两个发射器,你需要在另一个发射器上调用.emit()(即另一个模块正在监听的那个)。在module.js中,将emitter.emit('toServer');更改为server1.emit('toServer');,并相应地更改server.js中的代码。我还看到你在这里有一个循环依赖关系,每个模块都需要另一个模块并在其初始化中使用模块句柄 - 我不确定node.js如何处理它。 - jfriend00
@SuperCodeBrah - 如果这个回答解决了你的问题,请点击答案左侧的绿色勾号,以向社区表明你的问题已得到解答,并且你还将因遵循正确的程序而获得一些声望点数。 - jfriend00
显示剩余3条评论

26
为了让操作更为简单,我新建了一个名为Notifier.js的js文件,内容如下:
let EventEmitter = require('events').EventEmitter
let notifier = new EventEmitter()
module.exports = notifier

当我想要使用发射器时,我只需要要求导入Notifier.js文件并访问导出的值。例如:

randomFile.js

var notifier = require('Notifier.js')
notifier.on('myEvent', (message) => {
    console.log(message)
})

randomFile2.js

var notifier = require('Notifier.js')
notifier.emit('myEvent', 'Test Message')

0

我使用了上方@Mattew Cawley的回复来创建这个模块,对我来说效果很好。

import EventEmitter from "events";

class Notifer {
    #eventEmitter: EventEmitter;
    #DATA_BASE_CONNECTED = "DATABASE_CONNECTED";

    constructor() {
        this.#eventEmitter = new EventEmitter();
    }

    emitDbConnected(args: any) {
        this.#eventEmitter.emit(this.#DATA_BASE_CONNECTED, args);
    }
    onDbConnected(handler: EventListener) {
        this.#eventEmitter.on(this.#DATA_BASE_CONNECTED, handler);
    }
}

export default new Notifer();

现在这个模块可以像这样使用:

// module 1

import notifier from "path/to/notifier";
notifier.emitDbConnected("some args");

&

// module 2

import notifier from "path/to/notifier";
notifier.onDbConnected((args) => {});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接