summaryrefslogtreecommitdiff
path: root/lib/events.js
diff options
context:
space:
mode:
authorBrian White <mscdex@mscdex.net>2015-02-11 17:00:12 -0500
committerBen Noordhuis <info@bnoordhuis.nl>2015-02-11 23:06:26 +0100
commit7061669dba26337e47e258385dd58e400881dff1 (patch)
treef6e4032f2c52c3ee2b30c32f22e6f07b5b9650a8 /lib/events.js
parent630f63633490f60a292e62bb7a82bd9ea4de68c4 (diff)
downloadandroid-node-v8-7061669dba26337e47e258385dd58e400881dff1.tar.gz
android-node-v8-7061669dba26337e47e258385dd58e400881dff1.tar.bz2
android-node-v8-7061669dba26337e47e258385dd58e400881dff1.zip
events: optimize adding and removing of listeners
These optimizations result in >2x speedup in the ee-add-remove benchmark: * Don't mutate array.length when removing the last listener for an event * Don't bother checking max listeners if listeners isn't an array * Don't call delete when removing the last event in _events, just re-assign a new object instead PR-URL: https://github.com/iojs/io.js/pull/785 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Evan Lucas <evanlucas@me.com>
Diffstat (limited to 'lib/events.js')
-rw-r--r--lib/events.js114
1 files changed, 69 insertions, 45 deletions
diff --git a/lib/events.js b/lib/events.js
index 33bcc18533..02e7ac9514 100644
--- a/lib/events.js
+++ b/lib/events.js
@@ -30,8 +30,10 @@ EventEmitter.init = function() {
}
}
- if (!this._events || this._events === Object.getPrototypeOf(this)._events)
+ if (!this._events || this._events === Object.getPrototypeOf(this)._events) {
this._events = {};
+ this._eventsCount = 0;
+ }
this._maxListeners = this._maxListeners || undefined;
};
@@ -115,15 +117,18 @@ function emitMany(handler, isFn, self, args) {
EventEmitter.prototype.emit = function emit(type) {
var er, handler, len, args, i, events, domain;
var needDomainExit = false;
+ var doError = (type === 'error');
events = this._events;
- if (!events)
- events = this._events = {};
+ if (events)
+ doError = (doError && events.error == null);
+ else if (!doError)
+ return false;
domain = this.domain;
// If there is no 'error' event listener then throw.
- if (type === 'error' && !events.error) {
+ if (doError) {
er = arguments[1];
if (domain) {
if (!er)
@@ -189,39 +194,47 @@ EventEmitter.prototype.addListener = function addListener(type, listener) {
throw new TypeError('listener must be a function');
events = this._events;
- if (!events)
+ if (!events) {
events = this._events = {};
- else {
+ this._eventsCount = 0;
+ } else {
// To avoid recursion in the case that type === "newListener"! Before
// adding it to the listeners, first emit "newListener".
if (events.newListener) {
this.emit('newListener', type,
- typeof listener.listener === 'function' ?
- listener.listener : listener);
+ listener.listener ? listener.listener : listener);
+
+ // Re-assign `events` because a newListener handler could have caused the
+ // this._events to be assigned to a new object
+ events = this._events;
}
existing = events[type];
}
- if (!existing)
+ if (!existing) {
// Optimize the case of one listener. Don't need the extra array object.
existing = events[type] = listener;
- else if (typeof existing !== 'function')
- // If we've already got an array, just append.
- existing.push(listener);
- else
- // Adding the second element, need to change to array.
- existing = events[type] = [existing, listener];
-
- // Check for listener leak
- if (typeof existing !== 'function' && !existing.warned) {
- m = $getMaxListeners(this);
- if (m && m > 0 && existing.length > m) {
- existing.warned = true;
- console.error('(node) warning: possible EventEmitter memory ' +
- 'leak detected. %d %s listeners added. ' +
- 'Use emitter.setMaxListeners() to increase limit.',
- existing.length, type);
- console.trace();
+ ++this._eventsCount;
+ } else {
+ if (typeof existing === 'function') {
+ // Adding the second element, need to change to array.
+ existing = events[type] = [existing, listener];
+ } else {
+ // If we've already got an array, just append.
+ existing.push(listener);
+ }
+
+ // Check for listener leak
+ if (!existing.warned) {
+ m = $getMaxListeners(this);
+ if (m && m > 0 && existing.length > m) {
+ existing.warned = true;
+ console.error('(node) warning: possible EventEmitter memory ' +
+ 'leak detected. %d %s listeners added. ' +
+ 'Use emitter.setMaxListeners() to increase limit.',
+ existing.length, type);
+ console.trace();
+ }
}
}
@@ -254,7 +267,7 @@ EventEmitter.prototype.once = function once(type, listener) {
// emits a 'removeListener' event iff the listener was removed
EventEmitter.prototype.removeListener =
function removeListener(type, listener) {
- var list, events, position, length, i;
+ var list, events, position, i;
if (typeof listener !== 'function')
throw new TypeError('listener must be a function');
@@ -267,17 +280,18 @@ EventEmitter.prototype.removeListener =
if (!list)
return this;
- length = list.length;
- position = -1;
-
- if (list === listener ||
- (typeof list.listener === 'function' && list.listener === listener)) {
- delete events[type];
- if (events.removeListener)
- this.emit('removeListener', type, listener);
-
+ if (list === listener || (list.listener && list.listener === listener)) {
+ if (--this._eventsCount === 0)
+ this._events = {};
+ else {
+ delete events[type];
+ if (events.removeListener)
+ this.emit('removeListener', type, listener);
+ }
} else if (typeof list !== 'function') {
- for (i = length; i-- > 0;) {
+ position = -1;
+
+ for (i = list.length; i-- > 0;) {
if (list[i] === listener ||
(list[i].listener && list[i].listener === listener)) {
position = i;
@@ -289,8 +303,12 @@ EventEmitter.prototype.removeListener =
return this;
if (list.length === 1) {
- list.length = 0;
- delete events[type];
+ list[0] = undefined;
+ if (--this._eventsCount === 0) {
+ this._events = {};
+ return this;
+ } else
+ delete events[type];
} else {
spliceOne(list, position);
}
@@ -312,10 +330,15 @@ EventEmitter.prototype.removeAllListeners =
// not listening for removeListener, no need to emit
if (!events.removeListener) {
- if (arguments.length === 0)
+ if (arguments.length === 0) {
this._events = {};
- else if (events[type])
- delete events[type];
+ this._eventsCount = 0;
+ } else if (events[type]) {
+ if (--this._eventsCount === 0)
+ this._events = {};
+ else
+ delete events[type];
+ }
return this;
}
@@ -329,6 +352,7 @@ EventEmitter.prototype.removeAllListeners =
}
this.removeAllListeners('removeListener');
this._events = {};
+ this._eventsCount = 0;
return this;
}
@@ -336,12 +360,12 @@ EventEmitter.prototype.removeAllListeners =
if (typeof listeners === 'function') {
this.removeListener(type, listeners);
- } else if (Array.isArray(listeners)) {
+ } else if (listeners) {
// LIFO order
- while (listeners.length)
+ do {
this.removeListener(type, listeners[listeners.length - 1]);
+ } while (listeners[0]);
}
- delete events[type];
return this;
};