events: don't call once twice
authorTim Wood <washwithcare@gmail.com>
Tue, 12 Nov 2013 20:19:13 +0000 (12:19 -0800)
committerFedor Indutny <fedor.indutny@gmail.com>
Tue, 12 Nov 2013 23:21:04 +0000 (03:21 +0400)
Emitting an event within a `EventEmitter#once` callback of the same
event name will cause subsequent `EventEmitter#once` listeners of the
same name to be called multiple times.

    var emitter = new EventEmitter();

    emitter.once('e', function() {
      emitter.emit('e');
      console.log(1);
    });

    emitter.once('e', function() {
      console.log(2);
    });

    emitter.emit('e');

    // Output
    // 2
    // 1
    // 2

Fix the issue, by calling the listener method only if it was not
already called.

lib/events.js
test/simple/test-event-emitter-once.js

index 5188205..8c02a55 100644 (file)
@@ -170,9 +170,15 @@ EventEmitter.prototype.once = function(type, listener) {
   if (typeof listener !== 'function')
     throw TypeError('listener must be a function');
 
+  var fired = false;
+
   function g() {
     this.removeListener(type, g);
-    listener.apply(this, arguments);
+
+    if (!fired) {
+      fired = true;
+      listener.apply(this, arguments);
+    }
   }
 
   g.listener = listener;
index 1f0a207..2eaebcc 100644 (file)
@@ -47,3 +47,19 @@ process.on('exit', function() {
   assert.equal(1, times_hello_emited);
 });
 
+var times_recurse_emitted = 0;
+
+e.once('e', function() {
+       e.emit('e');
+       times_recurse_emitted++;
+});
+
+e.once('e', function() {
+       times_recurse_emitted++;
+});
+
+e.emit('e');
+
+process.on('exit', function() {
+  assert.equal(2, times_recurse_emitted);
+});