Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NodeJS wait for callback to finish on event emit

I have and application written in NodeJS with Express and am attempting to use EventEmitter to create a kind of plugin architecture with plugins hooking into the main code by listening to emitted events.

My problem comes when a plugin function makes an async request (to get data from mongo in this case) this causes the plugin code to finish and return control back to the original emitter which will then complete execution, before the async request in the plugin code finishes.

E.g:

Main App:

// We want to modify the request object in the plugin  
self.emit('plugin-listener', request);

Plugin:

// Plugin function listening to 'plugin-listener', 'request' is an arg
console.log(request);

// Call to DB (async)
this.getFromMongo(some_data, function(response){
    // this may not get called until the plugin function has finished!
}

My reason for avoiding a callback function back to the main code from the 'getFromMongo' function is that there may be 0 or many plugins listening to the event. Ideally I want some way to wait for the DB stuff to finish before returning control to the main app

Many Thanks

like image 326
Stuart Avatar asked Apr 07 '12 08:04

Stuart


Video Answer


2 Answers

Using the EventEmitter for plugin/middleware management is not ideal, because you cannot ensure that the listeners are executed sequentially, if they have asynchroneous code. This especially is a problem when these listeners interact with each other or the same data.

That's why i.e. connect/express middleware functions are stored in an array and executed one after the other, instead of using an EventEmitter; They each need to call a next(); function when they are done doing their task.

like image 139
klovadis Avatar answered Sep 20 '22 17:09

klovadis


Had the same kind of decision to make about some events that I sometime need to wait for before returning the response to the client and sometimes not (when not in an HTTP request context).

The easiest way for me was to add a callback as the last argument of the event.

Stuff.emit('do_some_stuff', data, data2, callback);

In the event check if there is a callback:

Stuff.on('do_some_stuff', function(data, data2, callback) {
  // stuff to do
  // ...
  if (typeof callback === "function") return callback(err, result);
});

I know that mixing event and callbacks can be messy but that work fine for what I need. The other solution I see is the one proposed by @redben: add an emit function at the end of the event. The problem when in a HTTP context is that you need unique keys so your events don't mess up if they do different stuff per user.

like image 45
antoineg Avatar answered Sep 17 '22 17:09

antoineg