Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
114 views
in Technique[技术] by (71.8m points)

javascript - Creating a Singleton EventEmittor in Nodejs & Express Project

I'm trying to create a Publisher-Subscriber pattern with Nodejs eventEmitters for my nodejs/express API project. My use-case is fairly simple:

  1. I want to emit events from various places in my project. For example, emit an event when a new user is created.
  2. Then I want a single place, where I listen to all the events and decide what actions to take.

This will require a singleton instance of eventEmitter because 'emit' and 'on' calls should happen on the same instance of 'event' object.

To achieve it, this is how my index.js looks:

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
}

This is how my middleware/controller function looks(where I'm emitting event):

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

And, this is how my event subscriber file looks:

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

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

Using this, the event is getting emitted, but the event subscriber is not getting trigerred. I think there is something wrong with the way I'm accessing req/app objects in my subscriber. Also, I'm getting

TypeError: eventListeners.eventSubscribers is not a function

error in my index.js file. Questions I have:

  1. Is this the right way to create a publisher/subscriber pattern?
  2. How do I access req/app object instances in my event subscriber?
  3. What's the best way to make sure event subscriber file is loaded when the app loads?
question from:https://stackoverflow.com/questions/65651829/creating-a-singleton-eventemittor-in-nodejs-express-project

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Assuming everything else is correctly setup by you,
I think the cause of the error that you mentioned

TypeError: eventListeners.eventSubscribers is not a function

is that no function is passed to the middleware i.e as a middleware, shouldn't you just pass the eventListeners.eventSubscribers like this

app.use(eventListeners.eventSubscribers);

instead of like this

app.use(eventListeners.eventSubscribers());

If we compare both expressions above, express is not able to pass the request object in the second case as you are calling it but this function call is returning nothing to the express i.e kind of void, but in first case since you passed the function object reference to express, it then calls it as middleware(as a callback later and passes the request object in it while calling it).

Alternatively I think you don't need to use middleware for using the EventEmitter, you can just keep a single global custom instance consisting of EventEmitter as it's property and all the different event you want to listen, and then just use them wherever you require them.You can use the EventEmitter as a property of this custom to emit event in whatever module you want to by importing this custom module there.

This is the working example below

index.js file/entry file

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!'))

This is our global singleton custom instance using EventEmitter as a property.

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();

and here we use our global CustomEventEmitter module in our request routes i.e 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;

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...