Dominik Palo Dominik Palo - 2 months ago 21
TypeScript Question

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

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

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);
}
Comments