1

I have the following code. It basically extends EventEmitter so that rather than emitting the event and forget about it, it actually collects the results.

I wrote it as an answer to this: EventEmitter implementation that allows you to get the listeners' results?

The problem with this code is that it assumes that every listener is asynchronous. If one of them is asynchronous, then async.series simply fails.

My idea is to wrap the listener with a function that checks if its last parameter is a function. It it's not, it should wrap it with a function that works in a similar fashion as async calls. But, I am failing miserably ad doing this.

Help?

var events = require('events');
var util = require('util');
var async = require('async');

// Create the enhanced EventEmitter
function AsyncEvents() {
    events.EventEmitter.call(this);
}
util.inherits(AsyncEvents, events.EventEmitter);

// Just a stub
AsyncEvents.prototype.onAsync = function( event, listener ){
  return this.on( event, listener );
}

// Async emit
AsyncEvents.prototype.emitAsync = function( ){

  var event,
  module,
  results = [],
  functionList = [],
  args,
  callback,
  eventArguments;

  // Turn `arguments` into a proper array
  args = Array.prototype.splice.call(arguments, 0);

  // get the `hook` and `hookArgument` variables 
  event = args.splice(0,1)[0]; // The first parameter, always the hook's name
  eventArguments = args;       // The leftovers, the hook's parameters

  // If the last parameter is a function, it's assumed
  // to be the callback
  if( typeof( eventArguments[ eventArguments.length-1 ] ) === 'function' ){
    callback = eventArguments.pop();   // The last parameter, always the callback
  }

  var listeners = this.listeners( event );

  listeners.forEach( function( listener ) {

      // Pushes the async function to functionList. Note that the arguments passed to invokeAll are
      // bound to the function's scope
      functionList.push( function( done ){
          listener.apply( this, Array.prototype.concat( eventArguments, done ) );
      } );
  });
  callback ? async.series( functionList, callback ) : async.series( functionList );
}

Here is an easy way to test it:

asyncEvents = new AsyncEvents();

asyncEvents.onAsync('one', function( paramOne1, done ){
  done( null, paramOne1 + ' --- ONE done, 1' );
});

asyncEvents.onAsync('one', function( paramOne2, done ){
  done( null, paramOne2 + ' --- ONE done, 2' );
});

// Uncomment this and async will fail
//asyncEvents.onAsync('one', function( paramOne3, done ){
//  return paramOne3 + ' --- ONE done, 3' ;
//});

asyncEvents.onAsync('two', function( paramTwo, done ){
  done( null, 'TWO done, 1' );
});


asyncEvents.emitAsync('one', 'P1', function( err, res ){
  console.log( err );
  console.log( res );
});

asyncEvents.emitAsync('two', 'P2', function( err, res ){
  console.log( err );
  console.log( res );
});

Thanks!

Community
  • 1
  • 1
Merc
  • 16,277
  • 18
  • 79
  • 122
  • As the other [response](http://stackoverflow.com/a/19218317/458093) to the linked question says, the event handlers are designed with the pattern "fire and forget". The whole node.js platform is designed this way. – Gabriel Llamas Oct 29 '13 at 10:10
  • There are cases where "fire and forget" is not what you want. Many cases. Now, having said this, please keep in scope with the question -- which is, collecting results from both async and sync functions. Thanks. – Merc Oct 29 '13 at 13:27
  • If you are aware of which functions are synchronous, you could simply wrap them in async functions. That may be easier than what you are trying to do. – Josh C. Oct 29 '13 at 14:50
  • I was interested in the answer both to improve the function, and to see if doing something like this would be even possible. Yes, wrapping functions would be easy... – Merc Oct 29 '13 at 15:15

1 Answers1

1

It simply cannot be done. End of story.

Merc
  • 16,277
  • 18
  • 79
  • 122