3

I have below code to add listeners on EventEmitter,

class MyClass {
  init() {
    this.listener = new EventEmitter();
    this.listener.on('eventName', this.onChange.bind(this));
  }
  onChange() {
    ...
  }
}

How can I remove the listener from EventEmitter? I can see two different ways:

this.listener.removeListener('eventName', this.onChange)
this.listener.removeListener('eventName', this.onChange.bind(this))

I wonder which one I should use. Whether I need the bind when I remove it?

Joey Yi Zhao
  • 37,514
  • 71
  • 268
  • 523
  • 2
    You'll need to remove the listener with the original reference. This means you'll have to store the anonymous function returned by `this.onChange.bind(this)` somewhere, such as a private property. – Caramiriel Mar 22 '18 at 22:33
  • Possible duplicate of [Node.js EventEmitter: How to bind a class context to the event listener and then remove this listener](https://stackoverflow.com/questions/39820651/node-js-eventemitter-how-to-bind-a-class-context-to-the-event-listener-and-then) – Caramiriel Mar 22 '18 at 22:34

2 Answers2

5

You can use bind() or you can use an Arrow Function in Node. Arrow Functions will inherit their execution context from the invoking context, which is similar to how bind() works and provides the same functionality.

this.listener.removeListener('eventName', this.onChange)

The above way of removing a listener won't work if it is being called in a removeListener(eventName) style function like the following:

const obj = new MyObject()
obj.init()
// This would blow up with a 
// ReferenceError saying you cant call removeListener() on undefined   
obj.listener.removeListener('event') 

Not sure the use of the init() function when you can use a constructor with the class syntax.

constructor() {
  this.listener = new EventEmitter()
  this.boundEventNameHandler = () => this.onChange()
  this.listener.on('eventName', this.boundEventNameHandler)
}

You can utilize the bound function for removing a listener within the context of a class using this.

let boundEventNameHandler = this.boundEventNameHandler
this.listener.removeListener('eventName', this.boundEventNameHandler)`

An example of this implemented looks like the following

const EventEmitter = require('events')

class MyObject {
  constructor () {
    this.listener = new EventEmitter()
    this.boundEventNameHandler = () => this.onChange()
    this.listener.on('eventName', this.boundEventNameHandler)
  }

  // I would reconsider making this a property of the MyObject class 
  // instead, make onChange() a local function outside the MyObject 
  // class definition because onChange in this example is only used by the 
  // event listener for 'eventName' 
  onChange () {
    console.log('event fired change')
  }

  removeListeners () {
    let boundEventNameHandler = this.boundEventNameHandler
    this.listener.removeListener('eventName', boundEventNameHandler)
  }
}
peteb
  • 18,552
  • 9
  • 50
  • 62
  • 1
    This is wrong. It doesn't work this way and it won't remove a listener because you essentially declare two different lambda functions in .on and .removeListener. – user0103 Oct 22 '18 at 18:50
  • 1
    @user0103 Good catch on the function pointer problem for `removeListener()`. Updated for storing a reference to the function pointer. To this extent, you can use either an arrow function or `.bind(this)` – peteb Oct 22 '18 at 19:34
0

For TypeScript / ESNext you can use this:

class MyClass {
    constructor() {
        this.emitter = new EventEmitter();

        this.emitter.on("event1", this.listener1);
        this.emitter.on("event2", this.listener2);

        this.emitter.removeListener("event1", this.listener1);
        this.emitter.removeListener("event2", this.listener2);

        this.emitter.emit("event1"); // no output
        this.emitter.emit("event2"); // no output
    }

    public listener1 = (e) => {
        console.dir(`listener1`);
    };

    public listener2 = (e) => {
        console.dir(`listener2`);
    };
}  

Basically you define properties and instantly assign bound functions to them.
This way you use the same function reference in .on and .removeListener.

user0103
  • 1,246
  • 3
  • 18
  • 36