【问题标题】:Creating a Singleton EventEmittor in Nodejs & Express Project在 Nodejs 和 Express 项目中创建单例 EventEmittor
【发布时间】:2021-04-15 12:12:16
【问题描述】:

我正在尝试使用 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
}

这就是我的中间件/控制器函数的样子(我在哪里发出事件):

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)
  }
}

而且,这就是我的 事件订阅者 文件的外观:

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

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

使用这个,事件被发出,但事件订阅者没有被触发。我认为我在订阅者中访问 req/app 对象的方式有问题。另外,我得到了

TypeError: eventListeners.eventSubscribers is not a function

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

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

【问题讨论】:

  • EventEmitter 应按如下方式导入:const { EventEmitter } = require ('events');。事件模块有其他类和属性,现在你没有按预期导入它。

标签: javascript node.js express eventemitter


【解决方案1】:

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

TypeError: eventListeners.eventSubscribers is not a function

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

app.use(eventListeners.eventSubscribers);

而不是这样

app.use(eventListeners.eventSubscribers());

如果我们比较上面的两个表达式,express 不能在第二种情况下传递请求对象,因为你正在调用它,但是这个函数调用没有返回 express 即一种 void,但在第一种情况下,因为你通过了要表达的函数对象引用,然后将其作为中间件调用(稍后作为回调,并在调用时将请求对象传递给其中)。

另外,我认为您不需要使用中间件来使用 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!'))

这是我们使用 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();

在这里,我们在请求路由中使用全局 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;

【讨论】:

  • 当我进行此更改时,我的 TypeError 消失了,但是当我从邮递员调用 CreateUser API 时,应用程序挂起。它进入我的 eventSubscriber 内部,但不会从其中出来。
  • @VikalpJain 你确定,你以正确的方式使用 EventEmitter,评论提到了另一种导入 EventEmitter 的方式。或者,我认为您不需要使用中间件来使用 EventEmitter,您可以只保留一个由 EventEmitter 组成的全局自定义实例作为它的属性以及您要侦听的所有不同事件,然后在 main 中使用它们入口文件。您可以使用 EventEmitter 作为此自定义的属性,通过在此处导入此自定义模块来在您想要的任何模块中发出事件。
  • 不,我不确定。事实上,我正在努力实现您在评论中描述的内容,但无法构建它。你能用一些代码来解释它吗?谢谢。
  • @VikalpJain 用可行的替代示例更新了答案
  • 非常感谢!这清除了很多东西。我会在早上试试这个然后回来!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-13
  • 1970-01-01
  • 2020-12-12
相关资源
最近更新 更多