npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

eventemittercollector

v0.3.15

Published

An implementation of EventEmitter that allows you to collect the listeners' results

Downloads

8

Readme

EventEmitterCollector

In Nodejs, the EventEmitter constructor is great in most situations: it fires up events, and allows others to listen for those events and act appropriately.

There is one use-case that is completely failed by node's EventEmitter: when you want to fire up an event, and you actually want to know what your listeners have to say about it. This is imensely useful for example:

  • In plugins. You fire up an event called gather files to send, and then each function registered with that EventEmitter will add files to the list of the ones to send. Note that
  • In cases where you want to check if an event was handled appropriately. For example, each listener could return true of false and you could decide what to do in case some of them didn't work (maybe warn the user?)

In these situations, and possibly others I haven't thought of, "welcome to EventEmitterCollector".

The API

The EventEmitterCollector constructor can be used just like EventEmitter, adding "Collect" to the methods' signature. Please note that EventEmitter's API is not 100% emulated (although it can be if there is enough interest in this module). At the moment, it supports:

  • onCollect() (with addListenerCollect() as an alias )
  • emitCollect()

It does not implement:

  • once()
  • removeListener()
  • removeAllListeners()
  • listeners()
  • setMaxListeners()

It also adds a specific way to emit events belonging to a specific module:

  • emitCollectModule()

Event emitter implementation that will actually collect the listener's results and return them

Usage

Here is the most basic usage of EventEmitterCollector:

var EventEmitterCollector = require('eventemittercollector');

var as = new EventEmitterCollector();

as.onCollect( 'event1', function( done ){
  console.log("Called event 'event1' (first listener)");
  done( null, 'event1, first listener' );
});

as.onCollect( 'event1', function( done ){
  console.log("Called event 'event1' (second listener)");
  done( null, 'event1, second listener' );
});

as.emitCollect( 'event1', function( err, results ){
  console.log( results );
});

The result:

Called event 'event1' (first listener)
Called event 'event1' (second listener)

[ { module: 'global', result: 'event1, first listener' },
  { module: 'global', result: 'event1, second listener' } ]

The results are grouped as an array of objects, where the first attribute is module (set by default as global) and the second one is result (which is whatever was returned by the listener).

If any one of the listeners sets the err variable, other missing listeners will not be invoked and the callback called by emit() will hav ethe error set:

var EventEmitterCollector = require('eventemittercollector');

var as = new EventEmitterCollector();

as.onCollect( 'event1', function( done ){
  console.log("Called event 'event1' (first listener)");
  done( new Error("Did not work") );
});

as.onCollect( 'event1', function( done ){
  console.log("Called event 'event1' (second listener)");
  done( null, 'event1, second listener' );
});

as.emitCollect( 'event1', function( err, results ){
  console.log( "Error:");
  console.log( err );
});

The result will be:

Called event 'event1' (first listener)
[Error: Did not work]

So, a single listener failing does stop the flow and does mean that something went horribly wrong and should be dealt with.

Using "modules"

EventEmitterCollector was first developed as a way to write "modules", and allow those modules to emit events identifying themselves as "that particular module".

If you are collecting results from listeners, there are many cases where you want a result to be associated to a specific listener.

So, you can associate a "module" (or, call it an "id") to a listener:

var EventEmitterCollector = require('eventemittercollector');

var as = new EventEmitterCollector();

as.onCollect( 'event1', 'someId', function( done ){
  console.log("Called event 'event1' (first listener)");
  done( null, 'event1, first listener' );
});

as.onCollect( 'event1', 'someId', function( done ){
  console.log("Called event 'event1' (second listener)");
  done( null, 'event1, second listener' );
});

as.emitCollect( 'event1', function( err, results ){
  console.log( results );
});

The result is:

Called event 'event1' (first listener)
Called event 'event1' (second listener)

[ { module: 'someId', result: 'event1, first listener' },
  { module: 'someId', result: 'event1, second listener' } ]

You can obviously have a mixture of module names as your listeners:

var EventEmitterCollector = require('eventemittercollector');

var as = new EventEmitterCollector();

as.onCollect( 'event1', 'module1', function( done ){
  console.log("Called event 'event1' (first listener)");
  done( null, 'event1, first listener' );
});

as.onCollect( 'event1', 'module2', function( done ){
  console.log("Called event 'event1' (second listener)");
  done( null, 'event1, second listener' );
});

as.onCollect( 'event1', function( done ){
  console.log("Called event 'event1' (third listener)");
  done( null, 'event1, third listener' );
});

as.emitCollect( 'event1', function( err, results ){
  console.log( results );
});

The result:

Called event 'event1' (first listener)
Called event 'event1' (second listener)
Called event 'event1' (third listener)

[ { module: 'module1', result: 'event1, first listener' },
  { module: 'module2', result: 'event1, second listener' },
  { module: 'global', result: 'event1, third listener' } ]

As you can see, the third listener didn't specify a module name. So, it defaulted to "global".

Parameters to events

You can emit an event and pass it arguments, which will then passed to your listeners. Watch out: the number of arguments must be the same for your emitters and your listeners.

So, for example:

var EventEmitterCollector = require('eventemittercollector');

var as = new EventEmitterCollector();

as.onCollect( 'event1', 'module1', function( n1, n2, done ){
  console.log("Called event 'event1'i (first listener)");
  console.log("n1: " + n1 + "; n2: " + n2 );
  done( null, 'event1, first listener' );
});

as.onCollect( 'event1', 'module2', function( n1, n2, done ){
  console.log("Called event 'event1' (second listener)");
  console.log("n1: " + n1 + "; n2: " + n2 );
  done( null, 'event1, second listener' );
});

as.onCollect( 'event1', function( n1, n2, done ){
  console.log("Called event 'event1' (third listener)");
  console.log("n1: " + n1 + "; n2: " + n2 );
  done( null, 'event1, third listener' );
});

as.emitCollect( 'event1', 10, 20, function( err, results ){
  console.log( results );
});

The result:

Called event 'event1' (first listener)
n1: 10; n2: 20
Called event 'event1' (second listener)
n1: 10; n2: 20
Called event 'event1' (third listener)
n1: 10; n2: 20
[ { module: 'module1', result: 'event1, first listener' },
  { module: 'module2', result: 'event1, second listener' },
  { module: 'global', result: 'event1, third listener' },

Emit only to specific modules

Sometimes, you might want to emit an event but decide to invoke listeners associated to specific modules. In these cases, you would use emitCollectModule():

var EventEmitterCollector = require('eventemittercollector');

var as = new EventEmitterCollector();

as.onCollect( 'event1', 'module1', function( done ){
  console.log("Called event 'event1' (first listener)");
  done( null, { a1: 'event1, first listener' }  );
});

as.onCollect( 'event1', 'module2', function( done ){
  console.log("Called event 'event1' (second listener)");
  done( null, { a1: 'event1, second listener' } );
});

as.onCollect( 'event1', 'module2', function( done ){
  console.log("Called event 'event1' (third listener)");
  done( null, { a2: 'event1, third listener' } );
});

as.onCollect( 'event1', function( done ){
  console.log("Called event 'event1' (fourth listener)");
  done( null, { a2: 'event1, fourth listener' } );
});

as.emitCollectModule( 'event1', 'module2', function( err, results ){
  console.log( results );
});

Result:

Called event 'event1' (second listener)
Called event 'event1' (third listener)

[ { module: 'module2', result: { a1: 'event1, second listener' } },
  { module: 'module2', result: { a2: 'event1, third listener' } },

Only the listeners associated to module2 were called.

Helper functions in results

The results array can be boring to manipulate. Sometimes, you just want the results without worrying about what the module name is, for example. The good news is that the results array comes with some handy helper functions:

onlyResults

This helper function will strip everything from the array, except the actual results. See:

var EventEmitterCollector = require('eventemittercollector');

var as = new EventEmitterCollector();

as.onCollect( 'event1', 'module1', function( done ){
  console.log("Called event 'event1' (first listener)");
  done( null, 'event1, first listener' );
});

as.onCollect( 'event1', 'module2', function( done ){
  console.log("Called event 'event1' (second listener)");
  done( null, 'event1, second listener' );
});

as.onCollect( 'event1', 'module2', function( done ){
  console.log("Called event 'event1' (third listener)");
  done( null, 'event1, third listener' );
});

as.onCollect( 'event1', function( done ){
  console.log("Called event 'event1' (fourth listener)");
  done( null, 'event1, fourth listener' );
});

as.emitCollect( 'event1', function( err, results ){
  console.log( results.onlyResults() );
});

The result:

Called event 'event1' (first listener)
Called event 'event1' (second listener)
Called event 'event1' (third listener)
Called event 'event1' (fourth listener)

[ 'event1, first listener',
  'event1, second listener',
  'event1, third listener',
  'event1, fourth listener' ]

groupByModule()

This helper function will group results by module:

var EventEmitterCollector = require('eventemittercollector');

var as = new EventEmitterCollector();

as.onCollect( 'event1', 'module1', function( done ){
  console.log("Called event 'event1' (first listener)");
  done( null, 'event1, first listener' );
});

as.onCollect( 'event1', 'module2', function( done ){
  console.log("Called event 'event1' (second listener)");
  done( null, 'event1, second listener' );
});

as.onCollect( 'event1', 'module2', function( done ){
  console.log("Called event 'event1' (third listener)");
  done( null, 'event1, third listener' );
});

as.onCollect( 'event1', function( done ){
  console.log("Called event 'event1' (fourth listener)");
  done( null, 'event1, fourth listener' );
});

as.emitCollect( 'event1', function( err, results ){
  console.log( results.groupByModule() );
});

The result:

Called event 'event1' (first listener)
Called event 'event1' (second listener)
Called event 'event1' (third listener)
Called event 'event1' (fourth listener)

{ module1: [ 'event1, first listener' ],
  module2: [ 'event1, second listener', 'event1, third listener' ],
  global: [ 'event1, fourth listener' ] }

Note that the result is an associative array, with each element being an array of results.

Afterword

You can argue that this module is not an EventEmitter at all: some argue that events are by definition "fire-and-forget". The API also differs from EventEmitter's,

I feel that the concepts behind EventEmitterCollectors are very similar to EventEmitter, and that fire-and-forget is only an opinion: wanting to know the results of an event is a fair enough requirement in some cases, even if it's just to know that all listeners actually managed to do what they were meant to do. This is especially true for cases where the success of each listener is critical (let alone the gathering of the results of each listener).