Search code examples
javascriptnode.jsclosureseventemitter

Overriding node eventEmitter.on with closure for logging not working


I'm trying to track down an issue in my SPA where an event is being triggered too many times. With that in mind, I wanted to override node's eventEmitter.on function to add some logging in then call the original. This lead me to this answer on SO https://stackoverflow.com/a/10427757/261682 about overriding window.alert and I came up with the following code:

// Ignore `state` here but it essentially means I can access `eventEmitter` all over my app.
var EventEmitter = ('events')   
state.eventEmitter = new EventEmitter()
state.eventEmitter.on = (function (original) {
    return function (name, callback) {
        console.log('On Called for %s', name)
        original(name, callback)
    }
})(state.eventEmitter.on)

This isn't working. The event isn't triggered at all and I don't see anything in the console.

What am I doing wrong?

Note: There are other ways for me to track my original issue down but I'm interested as to why my code above isn't working.


Solution

  • I tried your code and it mostly looks fine, though as I run it I get the following error:

    Cannot read property '_events' of undefined an internal error of EventEmitter

    This is because the way you pass the original .on() function it will lose the context (this argument) when being called.

    To avoid this bind the function to the event emitter by calling state.eventEmitter.on.bind(state.eventEmitter). I also noticed you forgot the return the result of the original function since on() is chainable.

    Furthermore if no one is listening to the events your overridden function won't be called as well.

    Here's the code with the modifications mentioned above

    var EventEmitter = require('events');
    state.eventEmitter = new EventEmitter();
    state.eventEmitter.on = (function (original) {
        return function (name, callback) {
            console.log('On Called for %s', name);
                return original(name, callback);
            }
    })(state.eventEmitter.on.bind(state.eventEmitter));
    
    state.eventEmitter.on('my_event', function () {
        console.log('my_event has been called');
    });
    
    state.eventEmitter.emit('my_event');
    

    With this got the following output:

    On Called for my_event
    my_event has been called