在 Nodejs 和 Express 项目中创建单例 EventEmittor

Vik*_*ain 5 javascript node.js express eventemitter

我正在尝试使用 Nodejs eventEmitters 为我的 nodejs/express API 项目创建发布者-订阅者模式。我的用例相当简单:

  1. 我想从项目中的各个地方发出事件。例如,在创建新用户时发出事件。
  2. 然后我想要一个地方,在那里我可以监听所有事件并决定采取什么行动。

这将需要 eventEmitter 的单例实例,因为“emit”和“on”调用应该发生在“event”对象的同一实例上。

为了实现它,我的index.js看起来是这样的:

const express = require('express')
const app = express()

const bodyParser = require('body-parser')
const routes = require('./api/routes')
const EventEmitter = require("events");
const eventEmitter = new EventEmitter();

const eventListeners = require('../src/subscribers/events');
//const eventSubs = new eventListeners();

app.use(eventListeners.eventSubscribers());

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

app.set('eventEmitter', eventEmitter);

app.get('/', (req, res) => res.send('App is working'))

app.use('/api', routes)

app.listen(3000, () => console.log('Example app listening on port 3000!'))

module.exports = {
  app
}
Run Code Online (Sandbox Code Playgroud)

这就是我的中间件/控制器函数的外观(我在其中发出事件):

const createUser = async (req, res, next) => {
  var user = req.body
  try {
    console.log('in controller');

    await newUser(user)

    console.log(req.app.get('eventEmitter'));

    req.app.get('eventEmitter').emit('USER_CREATED', {email: user.email, name: user.name})

    res.status(200).send({success: true});
    next()

  } catch(e) {
    console.log(e.message)
    res.sendStatus(500) && next(e)
  }
}
Run Code Online (Sandbox Code Playgroud)

而且,这就是我的事件订阅者文件的样子:

const eventSubscribers = (req) => {
    const eventEmitter = req.app.get('eventEmitter');

    eventEmitter.on('USER_CREATED', ({ email, name }) => {
        console.log('event fired and captured')
    })
}
Run Code Online (Sandbox Code Playgroud)

使用此方法,事件将被发出,但事件订阅者不会被触发。我认为我访问订阅者中的 req/app 对象的方式有问题。另外,我得到

TypeError: eventListeners.eventSubscribers is not a function
Run Code Online (Sandbox Code Playgroud)

我的 index.js 文件中出现错误。我有的问题:

  1. 这是创建发布者/订阅者模式的正确方法吗?
  2. 如何访问事件订阅者中的 req/app 对象实例?
  3. 确保应用程序加载时加载事件订阅者文件的最佳方法是什么?

mss*_*mss 2

假设您正确设置了其他所有内容,
我认为您提到的错误的原因

TypeError: eventListeners.eventSubscribers is not a function
Run Code Online (Sandbox Code Playgroud)

是没有函数传递给中间件,即作为中间件,你不应该像这样传递 eventListeners.eventSubscribers

app.use(eventListeners.eventSubscribers);
Run Code Online (Sandbox Code Playgroud)

而不是像这样

app.use(eventListeners.eventSubscribers());
Run Code Online (Sandbox Code Playgroud)

如果我们比较上面的两个表达式,在第二种情况下,express 无法在您调用它时传递请求对象,但此函数调用不会向express 返回任何内容,即类型为 void,但在第一种情况下,因为您传递了函数对象引用express,然后将其作为中间件调用(稍后作为回调并在调用时在其中传递请求对象)。

另外,我认为您不需要使用中间件来使用 EventEmitter,您可以只保留一个由 EventEmitter 组成的全局自定义实例作为它的属性和您想要侦听的所有不同事件,然后在需要的地方使用它们您可以使用 EventEmitter 作为此自定义的属性,通过导入此自定义模块在任何您想要的模块中发出事件。

这是下面的工作示例

index.js 文件/入口文件

const express = require('express')
const app = express()
const bodyParser = require('body-parser')
const routes = require("./userRoute");
//const eventSubs = new eventListeners();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

app.get('/', (req, res) => res.send('App is working'))

app.use('/api', routes);

app.listen(3000, () => console.log('Example app listening on port 3000!'))
Run Code Online (Sandbox Code Playgroud)

这是我们使用 EventEmitter 作为属性的全局单例自定义实例。

const { EventEmitter } = require("events");

class CustomEventEmitter {

    eventEmitter;

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

    getEventEmitter() {
        return this.eventEmitter;
    }

    initialize() {
        this.eventEmitter.on('USER_CREATED', ({ email, name }) => {
            console.log('event fired and captured with data', email, name);
        });
    }
}

module.exports = new CustomEventEmitter();
Run Code Online (Sandbox Code Playgroud)

在这里,我们在请求路由中使用全局 CustomEventEmitter 模块,即 userRoute.js

const router = require("express").Router();
const customEventEmitter = require("./CustomEventEmitter");

const createUser = async (req, res, next) => {
    var user = req.body
    try {
        console.log('in controller');

        // await newUser(user)

        customEventEmitter.getEventEmitter().emit('USER_CREATED', { email: user.email, name: user.name })

        res.status(200).send({ success: true });
        next()

    } catch (e) {
        console.log(e.message)
        res.sendStatus(500) && next(e)
    }
}

router.post("/createUser", createUser)

module.exports = router;
Run Code Online (Sandbox Code Playgroud)