Node.js EventEmitter: How to bind a class context to the event listener and then remove this listener

Dominik Palo picture Dominik Palo · Oct 2, 2016 · Viewed 8.7k times · Source

Is there a way to access to the class context in the event listener method with possibility to remove the listener?

Example 1:

import {EventEmitter} from "events";

export default class EventsExample1 {
    private emitter: EventEmitter;

    constructor(private text: string) {
        this.emitter = new EventEmitter();

        this.emitter.addListener("test", this.handleTestEvent);
        this.emitter.emit("test");
    }


    public dispose() {
        this.emitter.removeListener("test", this.handleTestEvent);
    }

    private handleTestEvent() {
        console.log(this.text);
    }
}

In this example removing the listener works, but the handleTestEvent()method has no access to the class context using this. this points to EventEmitter context, so this.text is not accessible.

Example 2:

import {EventEmitter} from "events";

export default class EventsExample2 {
    private emitter: EventEmitter;

    constructor(private text: string) {
        this.emitter = new EventEmitter();

        this.emitter.addListener("test", this.handleTestEvent.bind(this));
        this.emitter.emit("test");
    }

    public dispose() {
        this.emitter.removeListener("test", this.handleTestEvent);
    }

    private handleTestEvent() {
        console.log(this.text);
    }
}

In this example, I'm using the bind function to bind a context of the class to the event listener. Now handleTestEvent method has access to the class context using this => this.text is accessible, but listener cannot be removed with removeListener - it seems that bind creates a new anonymous function, so there is no reference to the bounded listener.

Example 3:

import {EventEmitter} from "events";

export default class EventsExample3 {
    private emitter: EventEmitter;

    constructor(private text: string) {
        this.emitter = new EventEmitter();

        this.emitter.addListener("test", () => this.handleTestEvent());
        this.emitter.emit("test");
    }

    public dispose() {
        this.emitter.removeListener("test", this.handleTestEvent);
    }

    private handleTestEvent() {
        console.log(this.text);
    }
}

In this example, I'm using an arrow function to preserve a context of the class in the event listener. handleTestEvent method has access to the class context using this, but listener cannot be removed (there is no reference to the bounded listener as in example 2).

I've tried an alternative event library - EventEmitter3 which has a support for custom context for events (class context can be passed as third parameter to the addListener function (this.emitter.addListener("test", this.handleTestEvent, this), it works perfectly, but I rather want to use the included EventEmitter from Node.js.

Answer

robertklep picture robertklep · Oct 2, 2016

You could do this in the constructor:

this.handleTestEvent = this.handleTestEvent.bind(this);
this.emitter.addListener("test", this.handleTestEvent);

If you want to use cutting edge, you can use the proposed bind operator as a shortcut:

this.handleTestEvent = ::this.handleTestEvent;
this.emitter.addListener("test", this.handleTestEvent);

Or use a property initializer to create a bound method:

constructor(private text: string) {
  this.emitter = new EventEmitter();

  this.emitter.addListener("test", this.handleTestEvent);
  this.emitter.emit("test");
}

handleTestEvent = () => {
  console.log(this.text);
}