events: add check for listeners length
authorTrevor Norris <trev.norris@gmail.com>
Thu, 14 Feb 2013 08:48:11 +0000 (00:48 -0800)
committerisaacs <i@izs.me>
Sat, 2 Mar 2013 01:36:47 +0000 (17:36 -0800)
Ability to return just the length of listeners for a given type, using
EventEmitter.listenerCount(emitter, event). This will be a lot cheaper
than creating a copy of the listeners array just to check its length.

doc/api/events.markdown
lib/_stream_readable.js
lib/events.js
lib/fs.js
lib/http.js
lib/readline.js
lib/stream.js

index 7998684..10b9ed0 100644 (file)
@@ -87,6 +87,12 @@ Returns an array of listeners for the specified event.
 
 Execute each of the listeners in order with the supplied arguments.
 
+
+### Class Method: EventEmitter.listenerCount(emitter, event)
+
+Return the number of listeners for a given event.
+
+
 ### Event: 'newListener'
 
 * `event` {String} The event name
index b9f8291..d22795a 100644 (file)
@@ -22,6 +22,7 @@
 module.exports = Readable;
 Readable.ReadableState = ReadableState;
 
+var EE = require('events').EventEmitter;
 var Stream = require('stream');
 var util = require('util');
 var StringDecoder;
@@ -451,7 +452,7 @@ Readable.prototype.pipe = function(dest, pipeOpts) {
   // however, don't suppress the throwing behavior for this.
   function onerror(er) {
     unpipe();
-    if (dest.listeners('error').length === 0)
+    if (EE.listenerCount(dest, 'error') === 0)
       dest.emit('error', er);
   }
   dest.once('error', onerror);
@@ -537,7 +538,7 @@ function flow(src) {
     state.flowing = false;
 
     // if there were data event listeners added, then switch to old mode.
-    if (src.listeners('data').length)
+    if (EE.listenerCount(src, 'data') > 0)
       emitDataEvents(src);
     return;
   }
index 223015e..69af3ae 100644 (file)
@@ -286,3 +286,14 @@ EventEmitter.prototype.listeners = function(type) {
   }
   return this._events[type].slice(0);
 };
+
+EventEmitter.listenerCount = function(emitter, type) {
+  var ret;
+  if (!emitter._events || !emitter._events[type])
+    ret = 0;
+  else if (typeof emitter._events[type] === 'function')
+    ret = 1;
+  else
+    ret = emitter._events[type].length;
+  return ret;
+};
index 1ad0b2e..d467c5e 100644 (file)
--- a/lib/fs.js
+++ b/lib/fs.js
@@ -1161,7 +1161,7 @@ fs.unwatchFile = function(filename, listener) {
     stat.removeAllListeners('change');
   }
 
-  if (stat.listeners('change').length === 0) {
+  if (EventEmitter.listenerCount(stat, 'change') === 0) {
     stat.stop();
     statWatchers[filename] = undefined;
   }
index 56d9aae..638cb0c 100644 (file)
@@ -1523,7 +1523,7 @@ function socketOnData(d, start, end) {
     var bodyHead = d.slice(start + bytesParsed, end);
 
     var eventName = req.method === 'CONNECT' ? 'connect' : 'upgrade';
-    if (req.listeners(eventName).length) {
+    if (EventEmitter.listenerCount(req, eventName) > 0) {
       req.upgradeOrConnect = true;
 
       // detach the socket
@@ -1874,7 +1874,7 @@ function connectionListener(socket) {
       var bodyHead = d.slice(start + bytesParsed, end);
 
       var eventName = req.method === 'CONNECT' ? 'connect' : 'upgrade';
-      if (self.listeners(eventName).length) {
+      if (EventEmitter.listenerCount(self, eventName) > 0) {
         self.emit(eventName, req, req.socket, bodyHead);
       } else {
         // Got upgrade header or CONNECT method, but have no handler.
@@ -1958,7 +1958,7 @@ function connectionListener(socket) {
         (req.httpVersionMajor == 1 && req.httpVersionMinor == 1) &&
         continueExpression.test(req.headers['expect'])) {
       res._expect_continue = true;
-      if (self.listeners('checkContinue').length) {
+      if (EventEmitter.listenerCount(self, 'checkContinue') > 0) {
         self.emit('checkContinue', req, res);
       } else {
         res.writeContinue();
index a2d7e1c..83b425c 100644 (file)
@@ -616,7 +616,7 @@ Interface.prototype._ttyWrite = function(s, key) {
 
     switch (key.name) {
       case 'c':
-        if (this.listeners('SIGINT').length) {
+        if (EventEmitter.listenerCount(this, 'SIGINT') > 0) {
           this.emit('SIGINT');
         } else {
           // This readline instance is finished
@@ -673,7 +673,7 @@ Interface.prototype._ttyWrite = function(s, key) {
 
       case 'z':
         if (process.platform == 'win32') break;
-        if (this.listeners('SIGTSTP').length) {
+        if (EventEmitter.listenerCount(this, 'SIGTSTP') > 0) {
           this.emit('SIGTSTP');
         } else {
           process.once('SIGCONT', (function(self) {
@@ -829,7 +829,7 @@ function emitKeypressEvents(stream) {
   stream._keypressDecoder = new StringDecoder('utf8');
 
   function onData(b) {
-    if (stream.listeners('keypress').length > 0) {
+    if (EventEmitter.listenerCount(stream, 'keypress') > 0) {
       var r = stream._keypressDecoder.write(b);
       if (r) emitKey(stream, r);
     } else {
@@ -846,7 +846,7 @@ function emitKeypressEvents(stream) {
     }
   }
 
-  if (stream.listeners('keypress').length > 0) {
+  if (EventEmitter.listenerCount(stream, 'keypress') > 0) {
     stream.on('data', onData);
   } else {
     stream.on('newListener', onNewListener);
index 481d764..098ff61 100644 (file)
 
 module.exports = Stream;
 
-var events = require('events');
+var EE = require('events').EventEmitter;
 var util = require('util');
 
-util.inherits(Stream, events.EventEmitter);
+util.inherits(Stream, EE);
 Stream.Readable = require('_stream_readable');
 Stream.Writable = require('_stream_writable');
 Stream.Duplex = require('_stream_duplex');
@@ -40,7 +40,7 @@ Stream.Stream = Stream;
 // part of this class) is overridden in the Readable class.
 
 function Stream() {
-  events.EventEmitter.call(this);
+  EE.call(this);
 }
 
 Stream.prototype.pipe = function(dest, options) {
@@ -90,7 +90,7 @@ Stream.prototype.pipe = function(dest, options) {
   // don't leave dangling pipes when there are errors.
   function onerror(er) {
     cleanup();
-    if (this.listeners('error').length === 0) {
+    if (EE.listenerCount(this, 'error') === 0) {
       throw er; // Unhandled stream error in pipe.
     }
   }