Golo Roden
Golo Roden

Reputation: 150624

Removing event listeners on a currently emitting event

I have the following sample app, written in Node.js:

'use strict';

var events = require('events'),
    util = require('util');

var EventEmitter = events.EventEmitter;

var Foo = function () {};

util.inherits(Foo, EventEmitter);

var foo = new Foo();

foo.once('x', function () {
  foo.removeAllListeners();
  console.log('Google!');
});

foo.once('x', function () {
  foo.removeAllListeners();
  console.log('Yahoo!');
});

foo.emit('x');

It prints:

Google!
Yahoo!

Now my question is: Apparently the removeAllListeners does not affect the event listeners that are currently bound to the event. Is this by random, or is this by intent? (I checked this out using 0.10.32 as well as 0.11.13)

The background of my question is: If I bind two event handlers to a stream's end event, and one of them calls removeAllListeners, does Node.js guarantee that both will always be run, or is this just by good luck?

Upvotes: 2

Views: 1575

Answers (1)

jfriend00
jfriend00

Reputation: 707238

In looking at the implementation of the .emit() method, it looks like once it starts processing an event and calling listeners, that event will not be affected by any code that calls removeAllListeners() so in your example both listeners will be called.

The code for .emit() makes a copy of the array of listeners before executing any of them so that once it starts executing one, it will execute all of them, even if they are removed during execution. Here's the relevant piece of code:

  } else if (util.isObject(handler)) {
    len = arguments.length;
    args = new Array(len - 1);
    for (i = 1; i < len; i++)
      args[i - 1] = arguments[i];

    listeners = handler.slice();
    len = listeners.length;
    for (i = 0; i < len; i++)
      listeners[i].apply(this, args);
  }

From the EventEmitter implementation here: https://github.com/joyent/node/blob/857975d5e7e0d7bf38577db0478d9e5ede79922e/lib/events.js#L120

In this piece of code, handler will be an array of listener functions. The line

listeners = handler.slice()

makes a copy of the listeners array before any listeners are executed. This is to be expected because iteration of that array can be messed up (duplicates or skips) if code is freely allowed to modify the array being iterated while it is being iterated. So, it freezes the set of listeners to be called before calling any of them.

Upvotes: 2

Related Questions