Skip to content
This repository

As per @isaacs and @ry's request, changes to pull request for events #1457

Closed
wants to merge 9 commits into from

10 participants

Paolo Fragomeni Felix Geisendörfer Koichi Kobayashi Marco Rogers Charlie Robbins Mikeal Rogers TJ Holowaychuk Trevor Norris Ben Noordhuis Arnout Kazemier
Paolo Fragomeni

this pull request now includes fixes from @koichik's code review.

added some commits August 01, 2011
Paolo Fragomeni

@koichik thank you for spending time on this and helping to carefully review!!

Felix Geisendörfer
Collaborator

I'm still -1 on merging this as a big patch like this.

Koichi Kobayashi
Collaborator

@hij1nx - Nice! but I think this warning is still necessary.

     emitter.on('foo', function() {
-      console.log(this.event); // => `foo`
-      emitter.emit('bar'); // emitting a new event changes the current event.
-      console.log(this.event); // => `bar`
+      emitter.emit('bar');
     });
Marco Rogers

In an effort to refocus this discussion, I've submitted a patch to enable simple support for global listeners. #1478

No offense to @hij1nx.

Paolo Fragomeni

This will not only conflict with my patch fundamentally but also my library. There are a lot of implications that are not considered here. And * is historically associated with namespace conventions. Since this intends to catch everything, then there will need to be a new semantic for single spaced namespaces. Using * in the event string suggests now that i may be able to do foo* or foo.* or perhaps (\d.*). @polotek, i believe your patch presents many potential conflicts.

Charlie Robbins

@felixge is your only gripe that it is big? It seems by that same logic you would have -1'ed the HTTP rewrite from @mikeal, yet you did not. Can you explain this discrepancy in your judgement?

Mikeal Rogers

EventEmitter isn't broken, http1 was :P

but in all seriousness, no code is hit more than EventEmitter. small performance patches have made node 20% faster because the code is so hot.

there is reason to be concerned by the volume of a change.

my biggest concern, which is why i want to see it broken in to separate patches, is that we don't have any room to discuss the API and possibly make changes because the conversation is highjacked by a debate about whether the entire thing should go in or not.

Charlie Robbins

@mikeal EE2 has solid benchmarks and there is no performance degredation running node with this patch.

Yes it is a large commit.
Yes it adds several features (which are critical imho, see: https://gist.github.com/1122318)
No its not slow
No it doesn't break anything
No it isn't enabled by default

There seems to be a lot of dissent from people who are simply concerned for the sake of being concerned; to near luddite proportions. It would be sad to me if this was dead in the water because of that kind of mentality

Felix Geisendörfer
Collaborator

@felixge is your only gripe that it is big? It seems by that same logic you would have -1'ed the HTTP rewrite from @mikeal, yet you did not. Can you explain this discrepancy in your judgement?

Afaik @mikael's rewrite did not introduce new API (I could be mistaken).

The reason I am -1 on this is because it adds multiple features to one of the most critical objects in the node core at once. I am all for evolving the EventEmitter, but this is too much, too fast. It's basically impossible for me to follow the whole discussion on the thing because the list of sub-discussions is endless. Breaking things down into smaller patches would allow for better discussions and probably a nicer end result.

--fg

Paolo Fragomeni

@ry @isaacs @felixge I am on board with the idea of breaking this up into smaller pull requests. I think I may be in the best position to do this based on the fact that i already have a ~50% performance optimization in my fork which lends itself to a ~5% overall performance increase for node (this is reduced to about 4% with the current features i've added) I've also spent hundreds of hours studying this issue. Can we start an email thread with the feature would you like to see first?

TJ Holowaychuk

does anyone know know how often people use "this" within the callbacks? might be too micro to consider (I didn't test in EE itself) but below are the consistent results I get for a call with no receiver vs .call(). I didn't even know "this" in those callbacks was significant so if no one else is really using it then it might be worth changing.

no receiver : 34721059 ops/s
       call : 29046426 ops/s
Charlie Robbins

@visionmedia That's a good question. If this is not used frequently enough in EE callbacks it could be a meaningful breaking change for future EE2 changes. As a couple of people have pointed out in EE2 this.event can be unreliable:

emitter.on('foo', function() {
  console.log(this.event); // => `foo`
  emitter.emit('bar'); // emitting a new event changes the current event.
  console.log(this.event); // => `bar`
});

If we make this breaking change, that wouldn't be the case anymore because we could bind to an object literal, { event: 'eventName' }

Mikeal Rogers

The http client rewrite did change the Agent API and in fact is a reverse incompatible change to the Agent API.

Most people don't use the Agent API directly so it's pretty low impact, but it is a breaking change.

Mikeal Rogers

can we add a test to the benchmark that shows instantiation time. it seems like we're only testing emits and I'm concerned that instantiation time could grow a little too much and we create a lot of these objects :)

Paolo Fragomeni

@mikeal which test are we talking about?

Marco Rogers

I was looking at this last night and changed the benchmarks.js file in EE2 a bit.

https://gist.github.com/1134827

It isolates the different functions a little better. With this test it looks like instantiation does take a bit hit because the constructor now does work. However, the performance improvements to dispatch in @hij1nx's patch are really impressive. We definitely want to take those.

FYI, this is my first time using benchmark.js. Good stuff. But let me know if I'm doing something wrong here.

Paolo Fragomeni

@polotek, im working on a patch witch is a combination of mine and yours. As i said before, my primary concerns with your patch were performance penalty and the lack of visibility into the actual event fired. I believe i have both of these issues resolved.

IIRC, the ctor is not required to execute. Please observe this micro-optimization found in several functions...

    this._events || init.call(this);

alot of the core codebase assumes an empty ctor or in a lot of cases completely disregards it; the above code accounts for that.

In regards to benchmarkjs it looks like you know what you're doing ;)

Marco Rogers

@hij1nx Yeah I saw that, but just realized that my test isn't really representative of usage. I'm creating emitters with the base constructor, but it's almost always subclassed right?

Paolo Fragomeni

Its frequently subclassed in core. Yet it is a 1:1 loss, excersized at different junctures.

Arnout Kazemier 3rd-Eden commented on the diff August 13, 2011
lib/events.js
((100 lines not shown))
  117
+      }
  118
+      else if(typeof tree._listeners === 'function') {
  119
+        tree._listeners = [tree._listeners, listener];
  120
+      }
  121
+      else if (isArray(tree._listeners)) {
  122
+
  123
+        tree._listeners.push(listener);
  124
+
  125
+        if (!tree._listeners.warned) {
  126
+
  127
+          var m = defaultMaxListeners;
  128
+
  129
+          if (m > 0 && tree._listeners.length > m) {
  130
+
  131
+            tree._listeners.warned = true;
  132
+            console.error('(node) warning: possible EventEmitter memory ' +
1
Arnout Kazemier
3rd-Eden added a note August 13, 2011

I know it's a legacy code, but shouldn't this be console.warn instead of console.error as it outputs a warning and not a 'real' error?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Mikeal Rogers

lately i've been seeing pipe() as a definition of flow control more than specifically about pushing events from one place to another. you can see this in the latest work i've done on request.

but, that doesn't mean that node-core should implement a new style of pipe(). I actually depend on core's pipe doing a great job of pushing data and handling push back in the pipe() that is beneath my domain specific pipe() logic.

i don't want to increase the surface area of core's pipe(), we should leave propogating additional events in the application layer above core. if in a year or so we have some solid examples then we can talk about adding something to core, but not before that.

Charlie Robbins

@mikeal I agree we should be judicious in how we push forward with this kind of "mad science". I'll try to whip up some more meaningful examples when I get some spare cycles.

I still think having ChildProcess instances inheriting from EventEmitter2 would be useful based on this example: https://gist.github.com/1122318

Trevor Norris
Collaborator

Quite a bit of discussion between this and #1478, just to drop off the face of the earth a year ago.

Just checked against current master (11a5119) and don't come close to merge-able. Will this code be fixed, or should it be closed?

Ben Noordhuis

Just checked against current master (11a5119) and don't come close to merge-able. Will this code be fixed, or should it be closed?

I'm closing it. This thread shows it was a controversial feature back in the day but people haven't spoken up since.

Ben Noordhuis bnoordhuis closed this November 23, 2012
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 9 unique commits by 1 author.

Aug 01, 2011
[lib] events update, adds propper namespacing and wildcard support edfe18f
[doc/api] updated docs to reflect the changes to events.js 189ebb5
Aug 03, 2011
[lib] trailing whitespace removed, named functions, internally used f…
…unctions privatized (reduced surface area), onAny returns , [doc] lines wrapped
82397be
Aug 04, 2011
[lib] emitting error should check this._all, listenersAny() implement…
…ed, 'un' changed to 'off' for API clarity, [doc/api] correction L#29, correction L#140, correction L#87, correction L#61, lines should not exceed 80 chars.
350520e
[doc] slight restructure 23db018
Merge https://github.com/joyent/node 1cfd6c6
Aug 05, 2011
copy error fixed. d716137
[lib] updates 'this.event' more frequently, [doc] updates to reflect …
…'this.event'
8891219
[lib] updates 'this.event' more frequently, [doc] updates to reflect …
…'this.event'
82ca8c8
This page is out of date. Refresh to see the latest.
142  doc/api/events.markdown
Source Rendered
... ...
@@ -1,59 +1,142 @@
1 1
 ## Events
2 2
 
3  
-Many objects in Node emit events: a `net.Server` emits an event each time
4  
-a peer connects to it, a `fs.readStream` emits an event when the file is
5  
-opened. All objects which emit events are instances of `events.EventEmitter`.
6  
-You can access this module by doing: `require("events");`
  3
+Many objects in Node emit events: a `net.Server` emits an event each time a
  4
+peer connects to it, a `fs.readStream` emits an event when the file is opened.
  5
+All objects which emit events are instances of `events.EventEmitter`. You can
  6
+access this module by doing: `require("events");`
7 7
 
8  
-Typically, event names are represented by a camel-cased string, however,
9  
-there aren't any strict restrictions on that, as any string will be accepted.
  8
+Typically, event names are represented by a camel-cased string, however, there
  9
+aren't any strict restrictions on that, as any string will be accepted.
10 10
 
11  
-Functions can then be attached to objects, to be executed when an event
12  
-is emitted. These functions are called _listeners_.
  11
+Functions can then be attached to objects, to be executed when an event is
  12
+emitted. These functions are called _listeners_.
13 13
 
14 14
 
15 15
 ### events.EventEmitter
16 16
 
17 17
 To access the EventEmitter class, `require('events').EventEmitter`.
18 18
 
19  
-When an `EventEmitter` instance experiences an error, the typical action is
20  
-to emit an `'error'` event.  Error events are treated as a special case in node.
  19
+When an `EventEmitter` instance experiences an error, the typical action is to
  20
+emit an `'error'` event.  Error events are treated as a special case in node.
21 21
 If there is no listener for it, then the default action is to print a stack
22 22
 trace and exit the program.
23 23
 
24  
-All EventEmitters emit the event `'newListener'` when new listeners are
25  
-added.
  24
+All EventEmitters emit the event `'newListener'` when new listeners are added.
26 25
 
27 26
 #### emitter.addListener(event, listener)
28 27
 #### emitter.on(event, listener)
29 28
 
30 29
 Adds a listener to the end of the listeners array for the specified event.
31 30
 
  31
+
32 32
     server.on('connection', function (stream) {
33 33
       console.log('someone connected!');
34 34
     });
35 35
 
  36
+
  37
+To use **Namespaces** and **Wildcards**, pass the `wildcard` option into the 
  38
+EventEmitter constructor.
  39
+
  40
+
  41
+    var emitter = new EventEmitter({ wildcard: true });
  42
+
  43
+
  44
+When namespaces/wildcards are enabled, events can either be specified as
  45
+strings (`foo.bar`) separated by a delimiter or arrays (`['foo', 'bar']`).
  46
+The delimiter that separates the event name into "spaces" or segments is
  47
+also configurable as a constructor option.
  48
+
  49
+
  50
+    var emitter = new EventEmitter({ wildcard: true, delimiter: ':' });
  51
+
  52
+
  53
+An event name can contain a wild card (the `*` character). If the event
  54
+name is a string, a wildcard may appear as `foo.*`. If the event name is
  55
+an array, the wildcard may appear as `['foo', '*']`.
  56
+
  57
+A `*` is not a catchall for all events, the code `emitter.on('*')` or
  58
+`emitter.emit('*')` will correlate with events that have a namespace length
  59
+of 1.
  60
+
  61
+If either of the above described events were passed to the `on` method,
  62
+subsequent emits such as the following would be observed...
  63
+
  64
+
  65
+    emitter.emit('foo.bazz');
  66
+    emitter.emit(['foo', 'bar']);
  67
+
  68
+
  69
+The name of the actual event that was fired is available by accessing
  70
+`this.event`. This is helpful when there are wildcards involved.
  71
+
  72
+
  73
+    emitter.on('*', function() {
  74
+      console.log(this.event); // output the current event name.
  75
+    });
  76
+
  77
+    emitter.on('foo', function() {
  78
+      emitter.emit('bar');
  79
+    });
  80
+
  81
+    emitter.emit('foo');
  82
+
  83
+
  84
+#### emitter.onAny(listener)
  85
+
  86
+Adds a listener that will be fired when any event is emitted.
  87
+
  88
+
  89
+    function f(value) { 
  90
+      console.log('This event listener will fire on any event.');
  91
+    };
  92
+
  93
+    server.onAny(f);
  94
+
  95
+
  96
+#### emitter.offAny(listener)
  97
+
  98
+Removes the listener that will be fired when any event is emitted.
  99
+
  100
+
  101
+    server.offAny(f);
  102
+
  103
+
36 104
 #### emitter.once(event, listener)
37 105
 
38  
-Adds a **one time** listener for the event. The listener is
39  
-invoked only the first time the event is fired, after which
40  
-it is removed.
  106
+Adds a **one time** listener for the event. The listener is invoked only the
  107
+first time the event is fired, after which it is removed.
41 108
 
42  
-    server.once('connection', function (stream) {
43  
-      console.log('Ah, we have our first user!');
  109
+
  110
+    server.once('connection', function (value) {
  111
+      console.log('Ah, we have our first value!');
44 112
     });
45 113
 
  114
+
  115
+#### emitter.many(event, timesToListen, listener)
  116
+
  117
+Adds a listener that will execute **n times** for the event before being
  118
+removed. The listener is invoked for n times that the event is fired,
  119
+after which it is removed.
  120
+
  121
+
  122
+    server.many('connection', 4, function (value) {
  123
+      console.log('Ah, we have captured a user!');
  124
+    });
  125
+
  126
+
46 127
 #### emitter.removeListener(event, listener)
  128
+#### emitter.off(event, listener)
47 129
 
48 130
 Remove a listener from the listener array for the specified event.
49 131
 **Caution**: changes array indices in the listener array behind the listener.
50 132
 
  133
+
51 134
     var callback = function(stream) {
52 135
       console.log('someone connected!');
53 136
     };
54 137
     server.on('connection', callback);
55 138
     // ...
56  
-    server.removeListener('connection', callback);
  139
+    server.off('connection', callback);
57 140
 
58 141
 
59 142
 #### emitter.removeAllListeners([event])
@@ -65,8 +148,8 @@ Removes all listeners, or those of the specified event.
65 148
 
66 149
 By default EventEmitters will print a warning if more than 10 listeners are
67 150
 added to it. This is a useful default which helps finding memory leaks.
68  
-Obviously not all Emitters should be limited to 10. This function allows
69  
-that to be increased. Set to zero for unlimited.
  151
+Obviously not all Emitters should be limited to 10. This function allows that
  152
+to be increased. Set to zero for unlimited.
70 153
 
71 154
 
72 155
 #### emitter.listeners(event)
@@ -74,14 +157,31 @@ that to be increased. Set to zero for unlimited.
74 157
 Returns an array of listeners for the specified event. This array can be
75 158
 manipulated, e.g. to remove listeners.
76 159
 
  160
+
77 161
     server.on('connection', function (stream) {
78 162
       console.log('someone connected!');
79 163
     });
  164
+    
80 165
     console.log(util.inspect(server.listeners('connection')); // [ [Function] ]
81 166
 
  167
+
  168
+#### emitter.listenersAny(event)
  169
+
  170
+Returns an array of listeners that are listening for any event that is
  171
+specified. This array can be manipulated, e.g. to remove listeners.
  172
+
  173
+
  174
+    server.onAny(function(value) {
  175
+      console.log('someone connected!');
  176
+    });
  177
+    
  178
+    console.log(server.listenersAny()[0]); // [ [Function] ] // connected!
  179
+
  180
+
82 181
 #### emitter.emit(event, [arg1], [arg2], [...])
83 182
 
84  
-Execute each of the listeners in order with the supplied arguments.
  183
+Execute each of the listeners that may be listening for the specified event
  184
+name in order with the list of arguments.
85 185
 
86 186
 #### Event: 'newListener'
87 187
 
415  lib/events.js
@@ -20,9 +20,129 @@
20 20
 // USE OR OTHER DEALINGS IN THE SOFTWARE.
21 21
 
22 22
 var isArray = Array.isArray;
  23
+var defaultMaxListeners = 10;
23 24
 
24  
-function EventEmitter() { }
25  
-exports.EventEmitter = EventEmitter;
  25
+function init() {
  26
+  this._events = new Object;
  27
+}
  28
+
  29
+function configure(conf) {
  30
+
  31
+  if (conf) {
  32
+    this.wildcard = conf.wildcard;
  33
+    this.delimiter = conf.delimiter || '.';
  34
+
  35
+    if (this.wildcard) {
  36
+      this.listenerTree = new Object;
  37
+    }
  38
+  }
  39
+}
  40
+
  41
+function EventEmitter(conf) {
  42
+  this._events = new Object;
  43
+  configure.call(this, conf);
  44
+}
  45
+
  46
+function searchListenerTree(handlers, type, tree, i) {
  47
+  if (!tree) {
  48
+    return;
  49
+  }
  50
+
  51
+  var listeners;
  52
+
  53
+  if (i === type.length && tree._listeners) {
  54
+    //
  55
+    // If at the end of the event(s) list and the tree has listeners
  56
+    // invoke those listeners.
  57
+    //
  58
+    if (typeof tree._listeners === 'function') {
  59
+      handlers && handlers.push(tree._listeners);
  60
+      return tree;
  61
+    } else {
  62
+      for (var leaf = 0, len = tree._listeners.length; leaf < len; leaf++) {
  63
+        handlers && handlers.push(tree._listeners[leaf]);
  64
+      }
  65
+      return tree;
  66
+    }
  67
+  }
  68
+
  69
+  if (type[i] === '*' || tree[type[i]]) {
  70
+    //
  71
+    // If the event emitted is '*' at this part
  72
+    // or there is a concrete match at this patch
  73
+    //
  74
+    if (type[i] === '*') {
  75
+      for (var branch in tree) {
  76
+        if (branch !== '_listeners' && tree.hasOwnProperty(branch)) {
  77
+          listeners = searchListenerTree(handlers, type, tree[branch], i+1);
  78
+        }
  79
+      }
  80
+      return listeners;
  81
+    }
  82
+
  83
+    listeners = searchListenerTree(handlers, type, tree[type[i]], i+1);
  84
+  }
  85
+
  86
+
  87
+  if (tree['*']) {
  88
+    //
  89
+    // If the listener tree will allow any match for this part,
  90
+    // then recursively explore all branches of the tree
  91
+    //
  92
+    searchListenerTree(handlers, type, tree['*'], i+1);
  93
+  }
  94
+
  95
+  return listeners;
  96
+};
  97
+
  98
+function growListenerTree(type, listener) {
  99
+
  100
+  type = typeof type === 'string' ? type.split(this.delimiter) : type.slice();
  101
+
  102
+  var tree = this.listenerTree;
  103
+  var name = type.shift();
  104
+
  105
+  while (name) {
  106
+
  107
+    if (!tree[name]) {
  108
+      tree[name] = new Object;
  109
+    }
  110
+
  111
+    tree = tree[name];
  112
+
  113
+    if (type.length === 0) {
  114
+
  115
+      if (!tree._listeners) {
  116
+        tree._listeners = listener;
  117
+      }
  118
+      else if(typeof tree._listeners === 'function') {
  119
+        tree._listeners = [tree._listeners, listener];
  120
+      }
  121
+      else if (isArray(tree._listeners)) {
  122
+
  123
+        tree._listeners.push(listener);
  124
+
  125
+        if (!tree._listeners.warned) {
  126
+
  127
+          var m = defaultMaxListeners;
  128
+
  129
+          if (m > 0 && tree._listeners.length > m) {
  130
+
  131
+            tree._listeners.warned = true;
  132
+            console.error('(node) warning: possible EventEmitter memory ' +
  133
+                          'leak detected. %d listeners added. ' +
  134
+                          'Use emitter.setMaxListeners() to increase limit.',
  135
+                          tree._listeners.length);
  136
+            console.trace();
  137
+          }
  138
+        }
  139
+      }
  140
+      return true;
  141
+    }
  142
+    name = type.shift();
  143
+  }
  144
+  return true;
  145
+};
26 146
 
27 147
 // By default EventEmitters will print a warning if more than
28 148
 // 10 listeners are added to it. This is a useful default which
@@ -30,20 +150,67 @@ exports.EventEmitter = EventEmitter;
30 150
 //
31 151
 // Obviously not all Emitters should be limited to 10. This function allows
32 152
 // that to be increased. Set to zero for unlimited.
33  
-var defaultMaxListeners = 10;
  153
+
34 154
 EventEmitter.prototype.setMaxListeners = function(n) {
35  
-  if (!this._events) this._events = {};
  155
+  this._events || init.call(this);
36 156
   this._events.maxListeners = n;
37 157
 };
38 158
 
  159
+EventEmitter.prototype.event = '';
  160
+
  161
+EventEmitter.prototype.once = function(event, fn) {
  162
+  this.many(event, 1, fn);
  163
+  return this;
  164
+};
  165
+
  166
+EventEmitter.prototype.many = function(event, ttl, fn) {
  167
+  var self = this;
  168
+
  169
+  if (typeof fn !== 'function') {
  170
+    throw new Error('many only accepts instances of Function');
  171
+  }
  172
+
  173
+  function listener() {
  174
+    if (--ttl === 0) {
  175
+      self.off(event, listener);
  176
+    }
  177
+    fn.apply(null, arguments);
  178
+  };
  179
+
  180
+  listener._origin = fn;
  181
+
  182
+  this.on(event, listener);
  183
+
  184
+  return self;
  185
+};
39 186
 
40 187
 EventEmitter.prototype.emit = function() {
  188
+  this._events || init.call(this);
  189
+
41 190
   var type = arguments[0];
  191
+
  192
+  if (type === 'newListener') {
  193
+    if (!this._events.newListener) { return false; }
  194
+  }
  195
+
  196
+  // Loop through the *_all* functions and invoke them.
  197
+  if (this._all) {
  198
+    var l = arguments.length;
  199
+    var args = new Array(l - 1);
  200
+    for (var i = 1; i < l; i++) args[i - 1] = arguments[i];
  201
+    for (i = 0, l = this._all.length; i < l; i++) {
  202
+      this.event = type;
  203
+      this._all[i].apply(this, args);
  204
+    }
  205
+  }
  206
+
42 207
   // If there is no 'error' event listener then throw.
43 208
   if (type === 'error') {
44  
-    if (!this._events || !this._events.error ||
45  
-        (isArray(this._events.error) && !this._events.error.length))
46  
-    {
  209
+    if (!this._all && (
  210
+      !this._events.error ||
  211
+      typeof this._events.error !== 'function' &&
  212
+      typeof this._events.error[0] !== 'function')) {
  213
+
47 214
       if (arguments[1] instanceof Error) {
48 215
         throw arguments[1]; // Unhandled 'error' event
49 216
       } else {
@@ -53,70 +220,81 @@ EventEmitter.prototype.emit = function() {
53 220
     }
54 221
   }
55 222
 
56  
-  if (!this._events) return false;
57  
-  var handler = this._events[type];
58  
-  if (!handler) return false;
  223
+  var handler;
59 224
 
60  
-  if (typeof handler == 'function') {
61  
-    switch (arguments.length) {
62  
-      // fast cases
63  
-      case 1:
64  
-        handler.call(this);
65  
-        break;
66  
-      case 2:
67  
-        handler.call(this, arguments[1]);
68  
-        break;
69  
-      case 3:
70  
-        handler.call(this, arguments[1], arguments[2]);
71  
-        break;
72  
-      // slower
73  
-      default:
74  
-        var l = arguments.length;
75  
-        var args = new Array(l - 1);
76  
-        for (var i = 1; i < l; i++) args[i - 1] = arguments[i];
77  
-        handler.apply(this, args);
  225
+  if(this.wildcard) {
  226
+    handler = [];
  227
+    var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice();
  228
+    searchListenerTree.call(this, handler, ns, this.listenerTree, 0);
  229
+  }
  230
+  else {
  231
+    handler = this._events[type];
  232
+  }
  233
+
  234
+  if (typeof handler === 'function') {
  235
+    this.event = type;
  236
+    if (arguments.length === 1) {
  237
+      handler.call(this);
78 238
     }
  239
+    else if (arguments.length > 1)
  240
+      switch (arguments.length) {
  241
+        case 2:
  242
+          handler.call(this, arguments[1]);
  243
+          break;
  244
+        case 3:
  245
+          handler.call(this, arguments[1], arguments[2]);
  246
+          break;
  247
+        // slower
  248
+        default:
  249
+          var l = arguments.length;
  250
+          var args = new Array(l - 1);
  251
+          for (var i = 1; i < l; i++) args[i - 1] = arguments[i];
  252
+          handler.apply(this, args);
  253
+      }
79 254
     return true;
80  
-
81  
-  } else if (isArray(handler)) {
  255
+  }
  256
+  else if (handler) {
82 257
     var l = arguments.length;
83 258
     var args = new Array(l - 1);
84 259
     for (var i = 1; i < l; i++) args[i - 1] = arguments[i];
85 260
 
86 261
     var listeners = handler.slice();
87 262
     for (var i = 0, l = listeners.length; i < l; i++) {
  263
+      this.event = type;
88 264
       listeners[i].apply(this, args);
89 265
     }
90 266
     return true;
91  
-
92  
-  } else {
93  
-    return false;
94 267
   }
95  
-};
96 268
 
97  
-// EventEmitter is defined in src/node_events.cc
98  
-// EventEmitter.prototype.emit() is also defined there.
99  
-EventEmitter.prototype.addListener = function(type, listener) {
100  
-  if ('function' !== typeof listener) {
101  
-    throw new Error('addListener only takes instances of Function');
102  
-  }
  269
+};
103 270
 
104  
-  if (!this._events) this._events = {};
  271
+EventEmitter.prototype.on = function(type, listener) {
  272
+  this._events || init.call(this);
105 273
 
106 274
   // To avoid recursion in the case that type == "newListeners"! Before
107 275
   // adding it to the listeners, first emit "newListeners".
108 276
   this.emit('newListener', type, listener);
109 277
 
  278
+  if(this.wildcard) {
  279
+    growListenerTree.call(this, type, listener);
  280
+    return this;
  281
+  }
  282
+
110 283
   if (!this._events[type]) {
111 284
     // Optimize the case of one listener. Don't need the extra array object.
112 285
     this._events[type] = listener;
113  
-  } else if (isArray(this._events[type])) {
114  
-
  286
+  }
  287
+  else if(typeof this._events[type] === 'function') {
  288
+    // Adding the second element, need to change to array.
  289
+    this._events[type] = [this._events[type], listener];
  290
+  }
  291
+  else if (isArray(this._events[type])) {
115 292
     // If we've already got an array, just append.
116 293
     this._events[type].push(listener);
117 294
 
118 295
     // Check for listener leak
119 296
     if (!this._events[type].warned) {
  297
+
120 298
       var m;
121 299
       if (this._events.maxListeners !== undefined) {
122 300
         m = this._events.maxListeners;
@@ -125,6 +303,7 @@ EventEmitter.prototype.addListener = function(type, listener) {
125 303
       }
126 304
 
127 305
       if (m && m > 0 && this._events[type].length > m) {
  306
+
128 307
         this._events[type].warned = true;
129 308
         console.error('(node) warning: possible EventEmitter memory ' +
130 309
                       'leak detected. %d listeners added. ' +
@@ -133,83 +312,159 @@ EventEmitter.prototype.addListener = function(type, listener) {
133 312
         console.trace();
134 313
       }
135 314
     }
136  
-  } else {
137  
-    // Adding the second element, need to change to array.
138  
-    this._events[type] = [this._events[type], listener];
139 315
   }
140  
-
141 316
   return this;
142 317
 };
143 318
 
144  
-EventEmitter.prototype.on = EventEmitter.prototype.addListener;
  319
+EventEmitter.prototype.onAny = function(fn) {
145 320
 
146  
-EventEmitter.prototype.once = function(type, listener) {
147  
-  if ('function' !== typeof listener) {
148  
-    throw new Error('.once only takes instances of Function');
  321
+  if(!this._all) {
  322
+    this._all = [];
149 323
   }
150 324
 
151  
-  var self = this;
152  
-  function g() {
153  
-    self.removeListener(type, g);
154  
-    listener.apply(this, arguments);
155  
-  };
156  
-
157  
-  g.listener = listener;
158  
-  self.on(type, g);
  325
+  if (typeof fn !== 'function') {
  326
+    throw new Error('onAny only accepts instances of Function');
  327
+  }
159 328
 
  329
+  // Add the function to the event listener collection.
  330
+  this._all.push(fn);
160 331
   return this;
161 332
 };
162 333
 
163  
-EventEmitter.prototype.removeListener = function(type, listener) {
164  
-  if ('function' !== typeof listener) {
  334
+EventEmitter.prototype.addListener = EventEmitter.prototype.on;
  335
+
  336
+EventEmitter.prototype.off = function(type, listener) {
  337
+  if (typeof listener !== 'function') {
165 338
     throw new Error('removeListener only takes instances of Function');
166 339
   }
167 340
 
168  
-  // does not use listeners(), so no side effect of creating _events[type]
169  
-  if (!this._events || !this._events[type]) return this;
  341
+  var handlers;
  342
+
  343
+  if(this.wildcard) {
  344
+    var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice();
  345
+    var leaf = searchListenerTree.call(this, null, ns, this.listenerTree, 0);
  346
+
  347
+    if('undefined' === typeof leaf) { return this; }
  348
+    handlers = leaf._listeners;
  349
+  }
  350
+  else {
  351
+    // does not use listeners(), so no side effect of creating _events[type]
  352
+    if (!this._events[type]) return this;
  353
+    handlers = this._events[type];
  354
+  }
170 355
 
171  
-  var list = this._events[type];
  356
+  if (isArray(handlers)) {
172 357
 
173  
-  if (isArray(list)) {
174 358
     var position = -1;
175  
-    for (var i = 0, length = list.length; i < length; i++) {
176  
-      if (list[i] === listener ||
177  
-          (list[i].listener && list[i].listener === listener))
178  
-      {
  359
+
  360
+    for (var i = 0, length = handlers.length; i < length; i++) {
  361
+      if (handlers[i] === listener ||
  362
+        (handlers[i].listener && handlers[i].listener === listener) ||
  363
+        (handlers[i]._origin && handlers[i]._origin === listener)) {
179 364
         position = i;
180 365
         break;
181 366
       }
182 367
     }
183 368
 
184  
-    if (position < 0) return this;
185  
-    list.splice(position, 1);
186  
-    if (list.length == 0)
  369
+    if (position < 0) {
  370
+      return this;
  371
+    }
  372
+
  373
+    if(this.wildcard) {
  374
+      leaf._listeners.splice(position, 1)
  375
+    }
  376
+    else {
  377
+      this._events[type].splice(position, 1);
  378
+    }
  379
+
  380
+    if (handlers.length === 0) {
  381
+      if(this.wildcard) {
  382
+        delete leaf._listeners;
  383
+      }
  384
+      else {
  385
+        delete this._events[type];
  386
+      }
  387
+    }
  388
+  }
  389
+  else if (handlers === listener ||
  390
+    (handlers.listener && handlers.listener === listener) ||
  391
+    (handlers._origin && handlers._origin === listener)) {
  392
+    if(this.wildcard) {
  393
+      delete leaf._listeners;
  394
+    }
  395
+    else {
187 396
       delete this._events[type];
188  
-  } else if (list === listener ||
189  
-             (list.listener && list.listener === listener))
190  
-  {
191  
-    delete this._events[type];
  397
+    }
192 398
   }
193 399
 
194 400
   return this;
195 401
 };
196 402
 
  403
+EventEmitter.prototype.offAny = function(fn) {
  404
+  var i = 0, l = 0, fns;
  405
+  if (fn && this._all && this._all.length > 0) {
  406
+    fns = this._all;
  407
+    for(i = 0, l = fns.length; i < l; i++) {
  408
+      if(fn === fns[i]) {
  409
+        fns.splice(i, 1);
  410
+        return this;
  411
+      }
  412
+    }
  413
+  } else {
  414
+    this._all = [];
  415
+  }
  416
+  return this;
  417
+};
  418
+
  419
+EventEmitter.prototype.removeListener = EventEmitter.prototype.off;
  420
+
197 421
 EventEmitter.prototype.removeAllListeners = function(type) {
198 422
   if (arguments.length === 0) {
199  
-    this._events = {};
  423
+    !this._events || init.call(this);
200 424
     return this;
201 425
   }
202 426
 
203  
-  // does not use listeners(), so no side effect of creating _events[type]
204  
-  if (type && this._events && this._events[type]) this._events[type] = null;
  427
+  if(this.wildcard) {
  428
+    var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice();
  429
+    var leaf = searchListenerTree.call(this, null, ns, this.listenerTree, 0);
  430
+
  431
+    if('undefined' === typeof leaf) { return this; }
  432
+    leaf._listeners = null;
  433
+  }
  434
+  else {
  435
+    if (!this._events[type]) return this;
  436
+    this._events[type] = null;
  437
+  }
205 438
   return this;
206 439
 };
207 440
 
208 441
 EventEmitter.prototype.listeners = function(type) {
209  
-  if (!this._events) this._events = {};
  442
+  if(this.wildcard) {
  443
+    var handlers = [];
  444
+    var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice();
  445
+    searchListenerTree.call(this, handlers, ns, this.listenerTree, 0);
  446
+    return handlers;
  447
+  }
  448
+
  449
+  this._events || init.call(this);
  450
+
210 451
   if (!this._events[type]) this._events[type] = [];
211 452
   if (!isArray(this._events[type])) {
212 453
     this._events[type] = [this._events[type]];
213 454
   }
214 455
   return this._events[type];
215 456
 };
  457
+
  458
+EventEmitter.prototype.listenersAny = function() {
  459
+
  460
+  if(this._all) {
  461
+    return this._all;
  462
+  }
  463
+  else {
  464
+    return [];
  465
+  }
  466
+
  467
+};
  468
+
  469
+exports.EventEmitter = EventEmitter;
  470
+
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.