Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

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

Closed
wants to merge 9 commits into from

10 participants

@hij1nx

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

hij1nx added some commits
@hij1nx

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

@felixge
Owner

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

@koichik
Owner

@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');
     });
@polotek

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

No offense to @hij1nx.

@hij1nx

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.

@indexzero

@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

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.

@indexzero

@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

@felixge
Owner

@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

@hij1nx

@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
tj commented

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
@indexzero

@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

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

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 :)

@hij1nx

@mikeal which test are we talking about?

@polotek

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.

@hij1nx

@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 ;)

@polotek

@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?

@hij1nx

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

@3rd-Eden 3rd-Eden commented on the diff
lib/events.js
((100 lines not shown))
+ }
+ else if(typeof tree._listeners === 'function') {
+ tree._listeners = [tree._listeners, listener];
+ }
+ else if (isArray(tree._listeners)) {
+
+ tree._listeners.push(listener);
+
+ if (!tree._listeners.warned) {
+
+ var m = defaultMaxListeners;
+
+ if (m > 0 && tree._listeners.length > m) {
+
+ tree._listeners.warned = true;
+ console.error('(node) warning: possible EventEmitter memory ' +

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

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.

@indexzero

@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

@trevnorris
Owner

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?

@bnoordhuis

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.

@bnoordhuis bnoordhuis closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Aug 1, 2011
Commits on Aug 2, 2011
Commits on Aug 4, 2011
  1. [lib] trailing whitespace removed, named functions, internally used f…

    hij1nx authored
    …unctions privatized (reduced surface area), onAny returns , [doc] lines wrapped
  2. [lib] emitting error should check this._all, listenersAny() implement…

    hij1nx authored
    …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.
  3. [doc] slight restructure

    hij1nx authored
  4. Merge https://github.com/joyent/node

    hij1nx authored
Commits on Aug 5, 2011
  1. copy error fixed.

    Hij1nx - Nodejitsu authored
This page is out of date. Refresh to see the latest.
Showing with 456 additions and 101 deletions.
  1. +121 −21 doc/api/events.markdown
  2. +335 −80 lib/events.js
View
142 doc/api/events.markdown
@@ -1,59 +1,142 @@
## Events
-Many objects in Node emit events: a `net.Server` emits an event each time
-a peer connects to it, a `fs.readStream` emits an event when the file is
-opened. All objects which emit events are instances of `events.EventEmitter`.
-You can access this module by doing: `require("events");`
+Many objects in Node emit events: a `net.Server` emits an event each time a
+peer connects to it, a `fs.readStream` emits an event when the file is opened.
+All objects which emit events are instances of `events.EventEmitter`. You can
+access this module by doing: `require("events");`
-Typically, event names are represented by a camel-cased string, however,
-there aren't any strict restrictions on that, as any string will be accepted.
+Typically, event names are represented by a camel-cased string, however, there
+aren't any strict restrictions on that, as any string will be accepted.
-Functions can then be attached to objects, to be executed when an event
-is emitted. These functions are called _listeners_.
+Functions can then be attached to objects, to be executed when an event is
+emitted. These functions are called _listeners_.
### events.EventEmitter
To access the EventEmitter class, `require('events').EventEmitter`.
-When an `EventEmitter` instance experiences an error, the typical action is
-to emit an `'error'` event. Error events are treated as a special case in node.
+When an `EventEmitter` instance experiences an error, the typical action is to
+emit an `'error'` event. Error events are treated as a special case in node.
If there is no listener for it, then the default action is to print a stack
trace and exit the program.
-All EventEmitters emit the event `'newListener'` when new listeners are
-added.
+All EventEmitters emit the event `'newListener'` when new listeners are added.
#### emitter.addListener(event, listener)
#### emitter.on(event, listener)
Adds a listener to the end of the listeners array for the specified event.
+
server.on('connection', function (stream) {
console.log('someone connected!');
});
+
+To use **Namespaces** and **Wildcards**, pass the `wildcard` option into the
+EventEmitter constructor.
+
+
+ var emitter = new EventEmitter({ wildcard: true });
+
+
+When namespaces/wildcards are enabled, events can either be specified as
+strings (`foo.bar`) separated by a delimiter or arrays (`['foo', 'bar']`).
+The delimiter that separates the event name into "spaces" or segments is
+also configurable as a constructor option.
+
+
+ var emitter = new EventEmitter({ wildcard: true, delimiter: ':' });
+
+
+An event name can contain a wild card (the `*` character). If the event
+name is a string, a wildcard may appear as `foo.*`. If the event name is
+an array, the wildcard may appear as `['foo', '*']`.
+
+A `*` is not a catchall for all events, the code `emitter.on('*')` or
+`emitter.emit('*')` will correlate with events that have a namespace length
+of 1.
+
+If either of the above described events were passed to the `on` method,
+subsequent emits such as the following would be observed...
+
+
+ emitter.emit('foo.bazz');
+ emitter.emit(['foo', 'bar']);
+
+
+The name of the actual event that was fired is available by accessing
+`this.event`. This is helpful when there are wildcards involved.
+
+
+ emitter.on('*', function() {
+ console.log(this.event); // output the current event name.
+ });
+
+ emitter.on('foo', function() {
+ emitter.emit('bar');
+ });
+
+ emitter.emit('foo');
+
+
+#### emitter.onAny(listener)
+
+Adds a listener that will be fired when any event is emitted.
+
+
+ function f(value) {
+ console.log('This event listener will fire on any event.');
+ };
+
+ server.onAny(f);
+
+
+#### emitter.offAny(listener)
+
+Removes the listener that will be fired when any event is emitted.
+
+
+ server.offAny(f);
+
+
#### emitter.once(event, listener)
-Adds a **one time** listener for the event. The listener is
-invoked only the first time the event is fired, after which
-it is removed.
+Adds a **one time** listener for the event. The listener is invoked only the
+first time the event is fired, after which it is removed.
- server.once('connection', function (stream) {
- console.log('Ah, we have our first user!');
+
+ server.once('connection', function (value) {
+ console.log('Ah, we have our first value!');
});
+
+#### emitter.many(event, timesToListen, listener)
+
+Adds a listener that will execute **n times** for the event before being
+removed. The listener is invoked for n times that the event is fired,
+after which it is removed.
+
+
+ server.many('connection', 4, function (value) {
+ console.log('Ah, we have captured a user!');
+ });
+
+
#### emitter.removeListener(event, listener)
+#### emitter.off(event, listener)
Remove a listener from the listener array for the specified event.
**Caution**: changes array indices in the listener array behind the listener.
+
var callback = function(stream) {
console.log('someone connected!');
};
server.on('connection', callback);
// ...
- server.removeListener('connection', callback);
+ server.off('connection', callback);
#### emitter.removeAllListeners([event])
@@ -65,8 +148,8 @@ Removes all listeners, or those of the specified event.
By default EventEmitters will print a warning if more than 10 listeners are
added to it. This is a useful default which helps finding memory leaks.
-Obviously not all Emitters should be limited to 10. This function allows
-that to be increased. Set to zero for unlimited.
+Obviously not all Emitters should be limited to 10. This function allows that
+to be increased. Set to zero for unlimited.
#### emitter.listeners(event)
@@ -74,14 +157,31 @@ that to be increased. Set to zero for unlimited.
Returns an array of listeners for the specified event. This array can be
manipulated, e.g. to remove listeners.
+
server.on('connection', function (stream) {
console.log('someone connected!');
});
+
console.log(util.inspect(server.listeners('connection')); // [ [Function] ]
+
+#### emitter.listenersAny(event)
+
+Returns an array of listeners that are listening for any event that is
+specified. This array can be manipulated, e.g. to remove listeners.
+
+
+ server.onAny(function(value) {
+ console.log('someone connected!');
+ });
+
+ console.log(server.listenersAny()[0]); // [ [Function] ] // connected!
+
+
#### emitter.emit(event, [arg1], [arg2], [...])
-Execute each of the listeners in order with the supplied arguments.
+Execute each of the listeners that may be listening for the specified event
+name in order with the list of arguments.
#### Event: 'newListener'
View
415 lib/events.js
@@ -20,9 +20,129 @@
// USE OR OTHER DEALINGS IN THE SOFTWARE.
var isArray = Array.isArray;
+var defaultMaxListeners = 10;
-function EventEmitter() { }
-exports.EventEmitter = EventEmitter;
+function init() {
+ this._events = new Object;
+}
+
+function configure(conf) {
+
+ if (conf) {
+ this.wildcard = conf.wildcard;
+ this.delimiter = conf.delimiter || '.';
+
+ if (this.wildcard) {
+ this.listenerTree = new Object;
+ }
+ }
+}
+
+function EventEmitter(conf) {
+ this._events = new Object;
+ configure.call(this, conf);
+}
+
+function searchListenerTree(handlers, type, tree, i) {
+ if (!tree) {
+ return;
+ }
+
+ var listeners;
+
+ if (i === type.length && tree._listeners) {
+ //
+ // If at the end of the event(s) list and the tree has listeners
+ // invoke those listeners.
+ //
+ if (typeof tree._listeners === 'function') {
+ handlers && handlers.push(tree._listeners);
+ return tree;
+ } else {
+ for (var leaf = 0, len = tree._listeners.length; leaf < len; leaf++) {
+ handlers && handlers.push(tree._listeners[leaf]);
+ }
+ return tree;
+ }
+ }
+
+ if (type[i] === '*' || tree[type[i]]) {
+ //
+ // If the event emitted is '*' at this part
+ // or there is a concrete match at this patch
+ //
+ if (type[i] === '*') {
+ for (var branch in tree) {
+ if (branch !== '_listeners' && tree.hasOwnProperty(branch)) {
+ listeners = searchListenerTree(handlers, type, tree[branch], i+1);
+ }
+ }
+ return listeners;
+ }
+
+ listeners = searchListenerTree(handlers, type, tree[type[i]], i+1);
+ }
+
+
+ if (tree['*']) {
+ //
+ // If the listener tree will allow any match for this part,
+ // then recursively explore all branches of the tree
+ //
+ searchListenerTree(handlers, type, tree['*'], i+1);
+ }
+
+ return listeners;
+};
+
+function growListenerTree(type, listener) {
+
+ type = typeof type === 'string' ? type.split(this.delimiter) : type.slice();
+
+ var tree = this.listenerTree;
+ var name = type.shift();
+
+ while (name) {
+
+ if (!tree[name]) {
+ tree[name] = new Object;
+ }
+
+ tree = tree[name];
+
+ if (type.length === 0) {
+
+ if (!tree._listeners) {
+ tree._listeners = listener;
+ }
+ else if(typeof tree._listeners === 'function') {
+ tree._listeners = [tree._listeners, listener];
+ }
+ else if (isArray(tree._listeners)) {
+
+ tree._listeners.push(listener);
+
+ if (!tree._listeners.warned) {
+
+ var m = defaultMaxListeners;
+
+ if (m > 0 && tree._listeners.length > m) {
+
+ tree._listeners.warned = true;
+ console.error('(node) warning: possible EventEmitter memory ' +

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
+ 'leak detected. %d listeners added. ' +
+ 'Use emitter.setMaxListeners() to increase limit.',
+ tree._listeners.length);
+ console.trace();
+ }
+ }
+ }
+ return true;
+ }
+ name = type.shift();
+ }
+ return true;
+};
// By default EventEmitters will print a warning if more than
// 10 listeners are added to it. This is a useful default which
@@ -30,20 +150,67 @@ exports.EventEmitter = EventEmitter;
//
// Obviously not all Emitters should be limited to 10. This function allows
// that to be increased. Set to zero for unlimited.
-var defaultMaxListeners = 10;
+
EventEmitter.prototype.setMaxListeners = function(n) {
- if (!this._events) this._events = {};
+ this._events || init.call(this);
this._events.maxListeners = n;
};
+EventEmitter.prototype.event = '';
+
+EventEmitter.prototype.once = function(event, fn) {
+ this.many(event, 1, fn);
+ return this;
+};
+
+EventEmitter.prototype.many = function(event, ttl, fn) {
+ var self = this;
+
+ if (typeof fn !== 'function') {
+ throw new Error('many only accepts instances of Function');
+ }
+
+ function listener() {
+ if (--ttl === 0) {
+ self.off(event, listener);
+ }
+ fn.apply(null, arguments);
+ };
+
+ listener._origin = fn;
+
+ this.on(event, listener);
+
+ return self;
+};
EventEmitter.prototype.emit = function() {
+ this._events || init.call(this);
+
var type = arguments[0];
+
+ if (type === 'newListener') {
+ if (!this._events.newListener) { return false; }
+ }
+
+ // Loop through the *_all* functions and invoke them.
+ if (this._all) {
+ var l = arguments.length;
+ var args = new Array(l - 1);
+ for (var i = 1; i < l; i++) args[i - 1] = arguments[i];
+ for (i = 0, l = this._all.length; i < l; i++) {
+ this.event = type;
+ this._all[i].apply(this, args);
+ }
+ }
+
// If there is no 'error' event listener then throw.
if (type === 'error') {
- if (!this._events || !this._events.error ||
- (isArray(this._events.error) && !this._events.error.length))
- {
+ if (!this._all && (
+ !this._events.error ||
+ typeof this._events.error !== 'function' &&
+ typeof this._events.error[0] !== 'function')) {
+
if (arguments[1] instanceof Error) {
throw arguments[1]; // Unhandled 'error' event
} else {
@@ -53,70 +220,81 @@ EventEmitter.prototype.emit = function() {
}
}
- if (!this._events) return false;
- var handler = this._events[type];
- if (!handler) return false;
+ var handler;
- if (typeof handler == 'function') {
- switch (arguments.length) {
- // fast cases
- case 1:
- handler.call(this);
- break;
- case 2:
- handler.call(this, arguments[1]);
- break;
- case 3:
- handler.call(this, arguments[1], arguments[2]);
- break;
- // slower
- default:
- var l = arguments.length;
- var args = new Array(l - 1);
- for (var i = 1; i < l; i++) args[i - 1] = arguments[i];
- handler.apply(this, args);
+ if(this.wildcard) {
+ handler = [];
+ var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice();
+ searchListenerTree.call(this, handler, ns, this.listenerTree, 0);
+ }
+ else {
+ handler = this._events[type];
+ }
+
+ if (typeof handler === 'function') {
+ this.event = type;
+ if (arguments.length === 1) {
+ handler.call(this);
}
+ else if (arguments.length > 1)
+ switch (arguments.length) {
+ case 2:
+ handler.call(this, arguments[1]);
+ break;
+ case 3:
+ handler.call(this, arguments[1], arguments[2]);
+ break;
+ // slower
+ default:
+ var l = arguments.length;
+ var args = new Array(l - 1);
+ for (var i = 1; i < l; i++) args[i - 1] = arguments[i];
+ handler.apply(this, args);
+ }
return true;
-
- } else if (isArray(handler)) {
+ }
+ else if (handler) {
var l = arguments.length;
var args = new Array(l - 1);
for (var i = 1; i < l; i++) args[i - 1] = arguments[i];
var listeners = handler.slice();
for (var i = 0, l = listeners.length; i < l; i++) {
+ this.event = type;
listeners[i].apply(this, args);
}
return true;
-
- } else {
- return false;
}
-};
-// EventEmitter is defined in src/node_events.cc
-// EventEmitter.prototype.emit() is also defined there.
-EventEmitter.prototype.addListener = function(type, listener) {
- if ('function' !== typeof listener) {
- throw new Error('addListener only takes instances of Function');
- }
+};
- if (!this._events) this._events = {};
+EventEmitter.prototype.on = function(type, listener) {
+ this._events || init.call(this);
// To avoid recursion in the case that type == "newListeners"! Before
// adding it to the listeners, first emit "newListeners".
this.emit('newListener', type, listener);
+ if(this.wildcard) {
+ growListenerTree.call(this, type, listener);
+ return this;
+ }
+
if (!this._events[type]) {
// Optimize the case of one listener. Don't need the extra array object.
this._events[type] = listener;
- } else if (isArray(this._events[type])) {
-
+ }
+ else if(typeof this._events[type] === 'function') {
+ // Adding the second element, need to change to array.
+ this._events[type] = [this._events[type], listener];
+ }
+ else if (isArray(this._events[type])) {
// If we've already got an array, just append.
this._events[type].push(listener);
// Check for listener leak
if (!this._events[type].warned) {
+
var m;
if (this._events.maxListeners !== undefined) {
m = this._events.maxListeners;
@@ -125,6 +303,7 @@ EventEmitter.prototype.addListener = function(type, listener) {
}
if (m && m > 0 && this._events[type].length > m) {
+
this._events[type].warned = true;
console.error('(node) warning: possible EventEmitter memory ' +
'leak detected. %d listeners added. ' +
@@ -133,83 +312,159 @@ EventEmitter.prototype.addListener = function(type, listener) {
console.trace();
}
}
- } else {
- // Adding the second element, need to change to array.
- this._events[type] = [this._events[type], listener];
}
-
return this;
};
-EventEmitter.prototype.on = EventEmitter.prototype.addListener;
+EventEmitter.prototype.onAny = function(fn) {
-EventEmitter.prototype.once = function(type, listener) {
- if ('function' !== typeof listener) {
- throw new Error('.once only takes instances of Function');
+ if(!this._all) {
+ this._all = [];
}
- var self = this;
- function g() {
- self.removeListener(type, g);
- listener.apply(this, arguments);
- };
-
- g.listener = listener;
- self.on(type, g);
+ if (typeof fn !== 'function') {
+ throw new Error('onAny only accepts instances of Function');
+ }
+ // Add the function to the event listener collection.
+ this._all.push(fn);
return this;
};
-EventEmitter.prototype.removeListener = function(type, listener) {
- if ('function' !== typeof listener) {
+EventEmitter.prototype.addListener = EventEmitter.prototype.on;
+
+EventEmitter.prototype.off = function(type, listener) {
+ if (typeof listener !== 'function') {
throw new Error('removeListener only takes instances of Function');
}
- // does not use listeners(), so no side effect of creating _events[type]
- if (!this._events || !this._events[type]) return this;
+ var handlers;
+
+ if(this.wildcard) {
+ var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice();
+ var leaf = searchListenerTree.call(this, null, ns, this.listenerTree, 0);
+
+ if('undefined' === typeof leaf) { return this; }
+ handlers = leaf._listeners;
+ }
+ else {
+ // does not use listeners(), so no side effect of creating _events[type]
+ if (!this._events[type]) return this;
+ handlers = this._events[type];
+ }
- var list = this._events[type];
+ if (isArray(handlers)) {
- if (isArray(list)) {
var position = -1;
- for (var i = 0, length = list.length; i < length; i++) {
- if (list[i] === listener ||
- (list[i].listener && list[i].listener === listener))
- {
+
+ for (var i = 0, length = handlers.length; i < length; i++) {
+ if (handlers[i] === listener ||
+ (handlers[i].listener && handlers[i].listener === listener) ||
+ (handlers[i]._origin && handlers[i]._origin === listener)) {
position = i;
break;
}
}
- if (position < 0) return this;
- list.splice(position, 1);
- if (list.length == 0)
+ if (position < 0) {
+ return this;
+ }
+
+ if(this.wildcard) {
+ leaf._listeners.splice(position, 1)
+ }
+ else {
+ this._events[type].splice(position, 1);
+ }
+
+ if (handlers.length === 0) {
+ if(this.wildcard) {
+ delete leaf._listeners;
+ }
+ else {
+ delete this._events[type];
+ }
+ }
+ }
+ else if (handlers === listener ||
+ (handlers.listener && handlers.listener === listener) ||
+ (handlers._origin && handlers._origin === listener)) {
+ if(this.wildcard) {
+ delete leaf._listeners;
+ }
+ else {
delete this._events[type];
- } else if (list === listener ||
- (list.listener && list.listener === listener))
- {
- delete this._events[type];
+ }
}
return this;
};
+EventEmitter.prototype.offAny = function(fn) {
+ var i = 0, l = 0, fns;
+ if (fn && this._all && this._all.length > 0) {
+ fns = this._all;
+ for(i = 0, l = fns.length; i < l; i++) {
+ if(fn === fns[i]) {
+ fns.splice(i, 1);
+ return this;
+ }
+ }
+ } else {
+ this._all = [];
+ }
+ return this;
+};
+
+EventEmitter.prototype.removeListener = EventEmitter.prototype.off;
+
EventEmitter.prototype.removeAllListeners = function(type) {
if (arguments.length === 0) {
- this._events = {};
+ !this._events || init.call(this);
return this;
}
- // does not use listeners(), so no side effect of creating _events[type]
- if (type && this._events && this._events[type]) this._events[type] = null;
+ if(this.wildcard) {
+ var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice();
+ var leaf = searchListenerTree.call(this, null, ns, this.listenerTree, 0);
+
+ if('undefined' === typeof leaf) { return this; }
+ leaf._listeners = null;
+ }
+ else {
+ if (!this._events[type]) return this;
+ this._events[type] = null;
+ }
return this;
};
EventEmitter.prototype.listeners = function(type) {
- if (!this._events) this._events = {};
+ if(this.wildcard) {
+ var handlers = [];
+ var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice();
+ searchListenerTree.call(this, handlers, ns, this.listenerTree, 0);
+ return handlers;
+ }
+
+ this._events || init.call(this);
+
if (!this._events[type]) this._events[type] = [];
if (!isArray(this._events[type])) {
this._events[type] = [this._events[type]];
}
return this._events[type];
};
+
+EventEmitter.prototype.listenersAny = function() {
+
+ if(this._all) {
+ return this._all;
+ }
+ else {
+ return [];
+ }
+
+};
+
+exports.EventEmitter = EventEmitter;
+
Something went wrong with that request. Please try again.