eventemitter2 / eventemitter2 Goto Github PK
View Code? Open in Web Editor NEWA nodejs event emitter implementation with namespaces, wildcards, TTL, works in the browser
License: MIT License
A nodejs event emitter implementation with namespaces, wildcards, TTL, works in the browser
License: MIT License
Currently there is no error handling for badly namespaced events such as:
event.ns1.
event..ns2
.ns1
.
as it stands it just eats the messages because there is no _event or _listeners associated with ''
It should throw an error.
Right now given the flat data structure used by the core EventEmitter
it is easy to detect if it has a listener for a given event:
e.g. https://github.com/nodejitsu/node-http-proxy/blob/master/lib/node-http-proxy.js#L765
OR
var events = require('events');
var emitter = new events.EventEmitter();
if (!emitter._events || emitter._events['some-event-name].length === 0) {
console.log('emitter does not have listeners for `some-event-name`');
}
The more complex data structure that EventEmitter2
introduces makes this more complex. Would be nice to have something similar
at the top of the reademe the event is being passed to listener the as the first argument,
but later it's only the value is being passed (although it is being ignored)
also, I notice that this is not backwards compatible with the old event emitter, because the old event emitter does not pass the event name as the first argument.
but great stuff!
Are there any plans to support a globstar wildcard in events? For example emitter.on(['foo', '**'], cb)
, for any event that starts with foo
, and may have zero or more sub-namespaces, or even emitter.on(['**', 'foo', '**'], cb)
, for any event that contains foo
. I've read in #72 that you're planning on making the wildcard system pluggable. Is that still relevant, and will it be possible to have globstar functionalty by those means?
emit() should return true in the following test case:
var EventEmitter = require('eventemitter2').EventEmitter2;
var ee = new EventEmitter();
ee.onAny(function() {
console.log("handled");
});
console.log("emit returned " + ee.emit("foo"));
I was trying to do the following :
var EventEmitter = require('eventemitter2').EventEmitter2;
var e = new EventEmitter();
e.on('i.*', function(){
console.log('hit i* event');
});
e.on('i.foo', function(){
console.log('hit i.foo');
});
e.emit('i.foo');
// outputs: hit i.foo
I expected this emit would be captured by both listeners. Is that wrong?
Current version on npm fails to install on NPM version 1.2.30
npm install eventemitter2
npm http GET https://registry.npmjs.org/eventemitter2
npm http 304 https://registry.npmjs.org/eventemitter2
npm WARN package.json [email protected] No repository field.
npm WARN package.json [email protected] 'repositories' (plural) Not supported.
npm WARN package.json Please pick one as the 'repository' field
npm WARN package.json [email protected] No repository field.
When there is no 'error' event listener, wildcard event listeners do not observe 'error' events and the process is killed, e.g.
var EventEmitter2 = require('eventemitter2').EventEmitter2;
var emitter = new EventEmitter2({
wildcard: true
, delimiter: ':'
, newListener: false
});
emitter.on('*', function () {
console.log('not reached');
});
emitter.emit('error', new Error());
console.log('not reached');
This script will not reach either of the console.log
statements because the unhandled 'error' event causes the program to exit.
On the other hand, when there is an 'error' event handler, the wildcard observer also observes the 'error' event, e.g
var EventEmitter2 = require('eventemitter2').EventEmitter2;
var emitter = new EventEmitter2({
wildcard: true
, delimiter: ':'
, newListener: false
});
emitter.on('*', function () {
console.log('wildcard handler called');
});
emitter.on('error', function () {
console.log('error handler called');
});
emitter.emit('error', new Error());
console.log('reached');
In this script, both both the 'error' handler and the '*' handler observe the error event, and all console.log
statements are reached.
It would be much more consistent to either:
EventEmitters have the ability to become complex and even confusing, documentation should provide a set of best practices to properly control growth/complexity. This section could address the Do's and Dont's of event emitters.
I would expect this listener to run only once, but it isn't removed after fired:
var type = 'test1.foo.bar';
var type2 = 'test1.foo.*';
var functionA = function() { test.ok(true, 'Event was fired'); };
emitter.once(type2, functionA);
emitter.emit(type);
emitter.emit(type);
test.expect(1);
This case is very similar to to unit test number 7 here: https://github.com/hij1nx/EventEmitter2/blob/master/test/wildcardEvents/ttl.js but the type and type2 are interchanged.
Is this behaviour intentional?
This seems undocumented.
It would be nice to have this EventEmitter2 available here:
http://component.io/search/emitter
@hij1nx if you are receptive to supporting component I will submit a pull-request
When running an app that is dependent on EventEmitter2 (like forever) you will get an error when you run it in a Joyent SmartMachine (runs on Solaris) because it thinks it's running in a browser context. If you remove the 'am I in browser' check it works fine.
See this SO issue:
Not sure why this has been done, but for some reason every event name is now being passed to the listener passed to on:
//create an event emitter for this app
var emitter = new EventEmitter2(),
events = {
rawTwitterDataFetched: 'rawTwitterDataFetched' //fired when raw data has been fetched from twitter
};
//handle incoming raw twitter data
emitter.on(events.rawTwitterDataFetched, function(data) {
console.log('should handle data: %o, arguments %o', data, arguments);
});
$.getJSON(generateRequestURL(), function(data) {
if (data) emitter.emit(events.rawTwitterDataFetched, data);
});
This logs should handle data: "rawTwitterDataFetched", arguments ["rawTwitterDataFetched", Object]
Note I'm running this in the browser.
What happend here?
Component, bower, volo, whatever.
Component is the only one I use but I think other people might appreciate the others.
Make EE2 faster by implementing optimization features in EE.
When somebody is using unique listeners for e.g. rpc-calls the EventEmitter2 implementation is leaking memory like hell. But when using static listener keys all works great.
The following test shows that the implementation is leaking memory very fast when using unique listener keys. After a few seconds the node process will need hundreds of MB. But when using static listener keys by changing leaksMemory
to false
the needed memory will stay constant.
var leaksMemory = true;
/**
* reproducible test code
*/
var cnt = 0;
function uniqid() {
return (leaksMemory) ? (++cnt) : 'static';
}
var emitter = new (require('eventemitter2').EventEmitter2)({
wildcard: true,
delimiter: '.',
maxListeners: 100
});
emitter.on('rpc-adder', function rpcResponder(rpcResponseId, numA, numB) {
emitter.emit(rpcResponseId, numA + numB);
});
function rpcClient() {
var rpcResponseId = 'rpc.' + uniqid();
var numA = Math.random(), numB = Math.random();
emitter.once(rpcResponseId, function rpcReceiveResponse(numResult) {
//console.log(numA + ' + ' + numB + ' = ' + numResult);
process.nextTick(rpcClient);
});
emitter.emit('rpc-adder', rpcResponseId, Math.random(), Math.random());
}
rpcClient();
The uniqid()
function will generate a unique key when leaksMemory == true
to show the leaking of memory, when it's false
static keys are used to show that leaking only occurs by using unique keys.
The function rpcClient()
is a rpc client creating a responseid it listens for receiving the rpc result. It'll publish a rpc function call with eventname rpc-adder
and subscribes for the answer. The eventname for the answer is a unique key to prevent eventname collisions of concurrent rpc calls by many clients. After every response a new rpc client call is started by using process.nextTick
to have a new call stack and make tons of rpc calls in a short time to quickly reproduce the problem.
Hello,
npm WARN [email protected] package.json: 'contributers' should probably be 'contributors'
EventEmitter2.listeners() errors when no arguments passed and wildcards enabled:
Error:
TypeError: Cannot call method 'slice' of undefined
at EventEmitter.listeners (/home/leonspencer/repos/node->cloud/node_modules/eventemitter2/lib/eventemitter2.js:561:77)
Code(eventemitter2.js):
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;
}
Consider checking for 'typeof type === undefined' might be an approach:
if(this.wildcard) {
var handlers = [];
if('typeof type === undefined') {
searchListenerTree.call(this, handlers, "*", this.listenerTree, 0);
} else {
var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice();
searchListenerTree.call(this, handlers, ns, this.listenerTree, 0);
}
return handlers;
}
If I have an EventEmitter and I want to emit an error in the constructor, how do I listen for an error event?
For instance:
new MyEmitter().on('error', function () {});
Since the error handler isn't bound until after the constructor finishes, the error
event isn't handled and exits the program.
Is there a way to either:
error
event to be handled after the constructor finisheserror
handler at the time of constructionCC: @crayo
While you can use setMaxListeners to change the maximum number of listeners, the same constructor parameter is ignored. The API documentation shows that we should be able to do
var server = new EventEmitter2({
maxListeners: 20
});
}
But the configure function does nothing with it
function configure(conf) {
if (conf) {
conf.delimiter && (this.delimiter = conf.delimiter);
conf.wildcard && (this.wildcard = conf.wildcard);
if (this.wildcard) {
this.listenerTree = new Object;
}
}
}
EventEmitter.listeners("") does not return all listeners. The wildcard should expand to all events. Currently, EventEmitter.listeners("") acts like EventEmitter.listenersAny(). If that is the desired approach, EventEmitter.listenersAny() is redundant and users can use EventEmitter.listeners("*").
However, EventEmitter.listeners("*") could return all listeners. And EventEmitter.listenersAny() should probably be deprecated and replaced with EventEmitter.listenersOnAny(), a more intuitive naming and avoiding redundancy.
IMHO
It should only be a slight change to make it compatible with advanced complation of google's javascript compiler. This could result in significant reductions in size (80% or so).
The main problem seems to be the way EventEmitter2 is made global in the browser. This is not properly detected by Closure, causing the object to be renamed while construction calls are not.
An example (test this at http://closure-compiler.appspot.com/home):
// ==ClosureCompiler==
// @compilation_level ADVANCED_OPTIMIZATIONS
// @output_file_name default.js
// ==/ClosureCompiler==
;!function(root, undefined) {
var EventEmitter2 = root.EventEmitter2 = function EventEmitter2(conf) {
// If *_listeners* is undefined invoke *initializeEvents* to set the
// default properties.
if (!this._listeners) this.initializeEvents(conf);
};
EventEmitter2.prototype.initializeEvents = function(conf) {
conf = conf || {};
var self = this;
conf.delimiter = conf.delimiter || '.';
if (conf.delimiter === '*' && conf.delimiter.length === 1) {
throw new Error('initializeEvents doesn\'t accept a "*" (wild-card) character as delimiter');
} else {
self._delimiter = conf.delimiter;
}
self._caseSensitive = typeof conf.caseSensitive === 'undefined' ? true : conf.caseSensitive;
self.setMaxListeners(conf.maxListeners);
self._listeners = {};
self._allListenerFn = [];
return self;
}
EventEmitter2.prototype.addListener = function(event, fn) {
var self = this;
// If *fn* is not a function throw an error. An *fn* that is not a function
// can not be invoked when an event is emitted and therefor is not allowed
// to be added.
if (typeof fn !== 'function') {
throw new Error('addListener only accepts instances of Function');
}
// If *_listeners* is undefined invoke *initializeEvents* to set the
// default properties.
if (!self._listeners) self.initializeEvents();
// Set variables.
var i = 0, l = 0,
delimiter = self._delimiter,
doubleDelimiter = delimiter + delimiter,
caseSensitive = self._caseSensitive,
listeners = self._listeners,
maxListeners = self._maxListeners,
event = caseSensitive ? event : event.toLowerCase(),
namespaced = ~event.indexOf(delimiter),
ns, exploreNs = listeners,
listenToEvent, eventListeners;
// If the event is namespaced loop through the namespaces, set seperate
// events for each namespace and set *listenToEvent* to the last
// namespace to attach the listener function to this event after the loop.
if (namespaced) {
// If an event starts or ends with a delimiter character or two or more
// delimiter characters are used after each other throw an error.
// Starting or ending an event with a delimiter character or combining
// characters creates empty namespaces which don't work and therefor are
// not allowed.
if (event[event.length - 1] === delimiter || event[0] === delimiter || ~event.indexOf(doubleDelimiter)) {
self.eventDelimiterError('addListener');
return false;
}
// Split the event into a namespace array for looping.
ns = event.split(delimiter);
// Loop through the namespaces.
for (i = 0, l = ns.length; i < l; i++) {
// If the namespace contains a wildcard character check if this
// character is the only one in the namespace. If it is not the only
// character in the namespace this namespace is invalid and therefor
// an error is thrown.
if (ns[i].indexOf('*') !== -1 && ns[i].length !== 1) {
self.eventWildcardError('addListener');
return false;
}
// If the event is undefined in *exploreNs* it means it doesn't exist
// in *listeners* so a new event should be created.
if (!exploreNs[ns[i]]) {
exploreNs[ns[i]] = {
_name: ns[i],
_fn: [],
_ns: {}
};
}
// If the loop is at the end set *listenToEvent* to the current
// namespace - which is the last one - to attach the listener to this
// event. If the loop is not at the end rebase *exploreNs* to loop the
// current event's namespaces.
if (i === ns.length - 1) {
listenToEvent = exploreNs[ns[i]];
} else {
exploreNs = exploreNs[ns[i]]._ns;
}
}
}
// If the event is not namespaced set the single event and set
// *listenToEvent* to this event to attach the listener to.
else {
// If the event contains a wildcard character check if this
// character is the only one in the event. If it is not the only
// character in the event this event is invalid and therefor
// an error is thrown.
if (event.indexOf('*') !== -1 && event.length !== 1) {
self.eventWildcardError('addListener');
return false;
}
// If the event is undefined in *listeners* it means it doesn't exist
// so a new event should be created.
if (!listeners[event]) {
listeners[event] = {
_name: event,
_fn: [],
_ns: {}
};
}
// Set *listenToEvent* to the current event to attach the listener to.
listenToEvent = listeners[event];
}
eventListeners = listenToEvent._fn;
// Signal that a new listener is being added.
self.emit('newListener', event, fn);
// If the max amount of listeners has been reached signal and return to
// cancel the addition of this new listener.
if (maxListeners > 0 && eventListeners.length >= maxListeners) {
self.emit('maxListeners', event);
return self;
}
// Add the function to the event listener collection.
eventListeners.push(fn);
return self;
};
EventEmitter2.prototype.onAny = function(fn) {
// If *fn* is not a function throw an error. An *fn* that is not a function
// can not be invoked when an event is emitted and therefor is not allowed
// to be added.
if (typeof fn !== 'function') {
throw new Error('onAny only accepts instances of Function');
}
// If *_listeners* is undefined invoke *initializeEvents* to set the
// default properties.
if (!this._listeners) this.initializeEvents();
// Add the function to the event listener collection.
this._allListenerFn.push(fn);
};
EventEmitter2.prototype.on = EventEmitter2.prototype.addListener;
EventEmitter2.prototype.once = function(event, fn) {
this.many(event, 1, fn);
return this;
};
EventEmitter2.prototype.many = function(event, ttl, fn) {
var self = this;
// If *fn* is not a function throw an error. An *fn* that is not a function
// can not be invoked when an event is emitted and therefor is not allowed
// to be added.
if (typeof fn !== 'function') {
throw new Error('many only accepts instances of Function');
}
function listener() {
if (--ttl == 0) {
self.removeListener(event, listener);
}
fn.apply(null, arguments);
};
listener._origin = fn;
self.addListener(event, listener);
return self;
};
EventEmitter2.prototype.emit = function(event) {
var self = this,
args = arguments,
i = 0, l = 0,
delimiter = self._delimiter,
doubleDelimiter = delimiter + delimiter,
caseSensitive = self._caseSensitive,
listeners = self._listeners,
event = caseSensitive ? event : event.toLowerCase(),
namespaced = ~event.indexOf(delimiter),
ns, exploreNs = [listeners], collectedListeners = [], collectedErrors = false,
invoked = false;
// If no listeners are defined the emit will not be able to invoke a
// function. Therefore return *invoked* to exit.
if (!listeners) return invoked;
// If the event is namespaced loop through the namespaces, find all the
// listeners for the namespaces and wildcards and add them to the
// *collectedListeners* array for listener invocation after the loop.
if (namespaced || event === '*') {
// If an event starts or ends with a delimiter character or two or more
// delimiter characters are used after each other throw an error.
// Starting or ending an event with a delimiter character or combining
// characters creates empty namespaces which don't work and therefor are
// not allowed.
if (event[event.length - 1] === delimiter || event[0] === delimiter || ~event.indexOf(doubleDelimiter)) {
self.eventDelimiterError('emit');
return false;
}
// Split the event into a namespace array for looping.
ns = event.split(delimiter);
// Loop through the namespaces.
for (i = 0; i < ns.length; i++) {
// If the namespace contains a wildcard character check if this
// character is the only one in the namespace. If it is not the only
// character in the namespace this namespace is invalid and therefor
// an error is thrown.
if (ns[i].indexOf('*') !== -1 && ns[i].length !== 1) {
self.eventWildcardError('addListener');
return false;
}
// While looping through the namespace array loop through *exploreNs*
// with *i* as well. This is basically the same as looping through the
// the different levels of *events*. *collectedNs* is used to collect
// all the namespaces that are going to be explored for the next level
// of *events*.
var currentNs = ns[i],
currentExploreNs = exploreNs[i],
collectedNs = [];
// Loop through the current level of *events*.
for (var key1 in currentExploreNs) {
// Set the current namespace in *events* that is being explored.
var exploredNs = currentExploreNs[key1],
name = caseSensitive ? exploredNs._name : exploredNs._name.toLowerCase();
// If there is a match for the namespace or a wildcard collect the
// next level of namespaces or collect the listeners of this
// particular namespace.
if (currentNs === '*' || (name === currentNs || name === '*')) {
// If the loop is at the end collect the listeners for this
// particular event and add them to the *collectedListeners* array
// for listener invocation after the loop. If the loop is not at
// the end collect the next level of namespaces.
if (i === ns.length - 1) {
var listeners = exploredNs._fn;
if (listeners.length > 0) {
collectedListeners = collectedListeners.concat(listeners);
}
} else {
for (var key2 in exploredNs._ns) {
collectedNs.push(exploredNs._ns[key2]);
}
}
}
// ...
if (currentNs === 'error' && name === 'error') {
collectedErrors = true;
}
}
// ...
if (currentNs === 'error' && !collectedErrors && (!self._allListenerFn || self._allListenerFn.length === 0)) {
// ...
if (arguments[1] instanceof Error) {
throw arguments[1];
} else {
throw new Error("Uncaught, unspecified 'error' event.");
}
return invoked;
}
exploreNs.push(collectedNs);
}
}
// If the event is not namespaced collect all the functions for this
// particular event and from the wildcard event and add them to
// the *collectedListeners* array for invocation.
else {
// If the event contains a wildcard character check if this
// character is the only one in the event. If it is not the only
// character in the event this event is invalid and therefor
// an error is thrown.
if (event.indexOf('*') !== -1 && event.length !== 1) {
self.eventWildcardError('addListener');
return false;
}
// ...
if (event === 'error') {
// ...
if (
!listeners ||
!listeners['error'] || listeners['error']._fn.length === 0 &&
(!self._allListenerFn || self._allListenerFn.length === 0)
) {
// ...
if (arguments[1] instanceof Error) {
throw arguments[1];
} else {
throw new Error("Uncaught, unspecified 'error' event.");
}
return invoked;
}
}
if (listeners[event] && listeners[event]._fn.length > 0) {
collectedListeners = collectedListeners.concat(listeners[event]._fn);
}
if (listeners['*'] && listeners['*']._fn.length > 0) {
collectedListeners = collectedListeners.concat(listeners['*']._fn);
}
}
// Loop through the collected functions and invoke them.
if (collectedListeners.length > 0) {
for (i = 0, l = collectedListeners.length; i < l; i++) {
collectedListeners[i].apply(self, args);
invoked = true;
}
}
// Loop through the *_allListenerFn* functions and invoke them.
if (self._allListenerFn && self._allListenerFn.length > 0) {
for (i = 0, l = self._allListenerFn.length; i < l; i++) {
self._allListenerFn[i].apply(self, args);
invoked = true;
}
}
return invoked;
};
EventEmitter2.prototype.emitAll = function() {};
EventEmitter2.prototype.removeListener = function(event, fn) {
var i = 0, l = 0, fns,
names, ns = this._listeners;
event = this._caseSensitive ? event : event.toLowerCase();
// fn not being a function does nothing
if (~event.indexOf(this._delimiter)) {
// namespaced, so try to find the ns
names = event.split(this._delimiter);
for (i = 0, l = names.length; i < l; i++) {
if (!ns || !ns[names[i]]) {
// if this namespace was never defined, return
return this;
}
if (i === names.length-1){
ns = ns[names[i]];
} else {
ns = ns[names[i]]._ns;
}
}
if (!ns._fn) {
// if this ns is empty for some reason
return this;
}
// above we stop 1 _ns before the last one
// so we havet go INTO the _ns, and grab it's _ns
fns = ns._fn;
for(var i = 0, l = fns.length; i < l; i++) {
if(fn === fns[i] || fn === fns[i]._origin) {
fns.splice(i, 1);
return this;
}
}
}
else {
if (ns[event]) {
// if this is not a namespaced event, just remove
fns = ns[event]._fn;
for (var i = 0, l = fns.length; i < l; i++) {
if(fn === fns[i] || fn === fns[i]._origin) {
fns.splice(i, 1);
return this;
}
}
}
}
// if we get here just return
return this;
};
EventEmitter2.prototype.un = EventEmitter2.prototype.removeListener;
EventEmitter2.prototype.unAny = function(fn) {
var i = 0, l = 0, fns;
if (fn && this._allListenerFn && this._allListenerFn.length > 0) {
fns = this._allListenerFn;
for(i = 0, l = fns.length; i < l; i++) {
if(fn === fns[i]) {
fns.splice(i, 1);
return this;
}
}
} else {
this._allListenerFn = [];
}
return this;
};
EventEmitter2.prototype.removeAllListeners = function(event) {
var listeners, i, l;
// if there is an event, then remove every listener on that listener
if (!event) {
this._listeners = {};
this._allListenerFn = [];
}
else {
listeners = this.listeners(event);
for (i = 0, l = listeners.length; i < l; i++) {
this.removeListener(event, listeners[i]);
}
}
return this;
};
EventEmitter2.prototype.setMaxListeners = function(n) {
this._maxListeners = n === 0 ? 0 : n || 10;
return this;
};
EventEmitter2.prototype.listeners = function(event) {
var ns = this._listeners,
i = 0, l = 0, fns,
names,
toReturn;
// case sensititivty
event = this._caseSensitive ? event : event.toLowerCase();
// check if we are namespaced
if (~event.indexOf(this._delimiter)) {
// namespaced so, find it
names = event.split(this._delimiter);
for (i = 0, l = names.length; i < l; i++) {
if (!ns || !ns[names[i]]) {
// if this namespace was never defined, return
return [];
}
if (i === names.length-1){
ns = ns[names[i]];
} else {
ns = ns[names[i]]._ns;
}
}
return ns._fn || [];
}
else {
if (!ns[event]) {
return [];
}
return ns[event]._fn || [];
}
};
EventEmitter2.prototype.listenersAny = function() {
return this._allListenerFn;
}
EventEmitter2.prototype.eventDelimiterError = function(fn) {
throw new Error(fn + ' doesn\'t accept empty namespaces or events starting or ending with a "' + this._delimiter + '" (delimiter) character');
};
EventEmitter2.prototype.eventWildcardError = function(fn) {
throw new Error(fn + ' doesn\'t accept events starting, ending or containing a "*" (wildcard) character');
};
}(typeof exports === 'undefined' ? window : exports);
//now create an emitter
var emitter = new EventEmitter2();
Results in the following output:
(function(b){b=b.f=function(a){this.b||this.d(a)};b.prototype.d=function(a){a=a||{};a.a=a.a||".";if(a.a==="*"&&a.a.length===1)throw Error('initializeEvents doesn\'t accept a "*" (wild-card) character as delimiter');else this.i=a.a;this.h=typeof a.c==="undefined"?!0:a.c;this.e(a.k);this.b={};this.g=[]};b.prototype.e=function(a){this.j=a===0?0:a||10}})(typeof exports==="undefined"?window:exports);new EventEmitter2;
Obviously, the call new EventEmitter2;
should become new f;
.
Example code:
a = new EventEmitter2()
a.once('x', function(){console.log(this.event);}) // console: undefined
a.many('x', 2, function(){console.log(this.event);}) // console: undefined
a.on('x', function(){console.log(this.event);}) // console: x
a.emit('x')
The native Node.js EventEmitter can be mixed in or used as inheritor for other objects. Using the constructor of EventEmitter2 to initialize and setup some of the basic properties prevents that from being possible (as far as my knowledge goes).
In the examples in the documentation you are using EventEmitter2 as the base object to "create" a new server, this however is not a real server but just an EventEmitter so that's why you probably haven't run into this issue yet :)
Looking into the core API's of Node.js, the native EventEmitter is inherited with a utility function to basically copy it's properties / functions to the other object. For an example see:
https://github.com/joyent/node/blob/master/lib/http.js#L1142
I would like to be able to do this with EventEmitter2 as well.
Do you agree if I fix EventEmitter2 so it can be used as the native EventEmitter and be mixed in with other objects using the utility function?
In the readme, under "Differences", I noticed:
var server = EventEmitter2({ // <------------ here
wildcard: true, // should the event emitter use wildcards.
delimiter: '::', // the delimiter used to segment namespaces, defaults to `.`.
maxListeners: 20, // the max number of listeners that can be assigned to an event, defaults to 10.
});
is missing a "new" keyword. I tried it without the new keyword and it didn't work, so I'm guessing this is a typo?
To make a release for bower one simply needs to push a git tag to github. I see that npm registry has all versions 0.4.12
and 0.4.13
for example, but these are missing from bower, because tags are not pushed to github.
Sometimes two listeners will bind to an event, where the execution order of the listeners matters.
Are there any plans to add support for listener order in EventEmitter2?
So my thoughts on how this can be accomplish is:
.on(listenerName, [listenerPriority,] listenerCallback)
listenerOrder: {'eventName': ['pluginNameThatShouldExecuteFirst', 'pluginNameThatShouldExecuteLast']}
Thoughts?
v8 introduced new things, so much like Pokemon, you'll need to catch 'em all.
Patch to follow!
I'm trying to use EE2 with stitch, and in order to make it work on the browser I had to add window.process = { "title": "hello world"}
before issuing the require statement.
Why checking the window object existance is not enough?
Let's say I want to run my server when it has been configured and when socketio has been configured.
This could be done like so:
ee2.once('server.configured', function(server) {
ee2.once('socketio.configured', function(io) {
server.listen(3000);
});
});
Just giving some random example. Now, it is possible that socketio.configured fires before server.configured fires. In this case, the above would not work. Thus I propose a new API that allows one to catch parallel events (that can be emitted in any order). Eg.:
ee2.onceMulti('server.configured', 'socketio.configured', function(server, io) {
server.listen(3000);
});
Any remarks?
Here's a gist showing the problem: https://gist.github.com/3817629
I haven't looked at the code, but it seems removeAllListeners with no arguments does not work when used with the wildcard option. It works fine (in this simple example) with no wildcard support.
Added test skeleton, needs to have tests written.
Be able to listen to every possible event without having to include a namespace.
currently you have to event.on(':',...) but should be able to event.on('*',...)
This is similar to #31 but distinct.
Can be feature request.
Look at code below.
var EventEmitter2 = require('eventemitter2').EventEmitter2;
var game = new EventEmitter2;\
function endGame() { console.log('THE END'); }
game.once(['player:quit', 'player:disconnect'], function () { endGame() });
//this does nothing - but should call endGame and unregister it
game.emit('player:quit');
//thid does nothing - but should'n call endGame because of previous call
game.emit('player:disconnect');
// this call endGame - this give no sense to me
game.emit(['player:quit', 'player:disconnect']);
I believe an array given to once shold be handled as one of given. There is no sense to listen to given array.
Is the statements below not expected to work?
Currently they don't for 'master'. Am i missing something?
// basic.js
var EventEmitter2 = require('../EventEmitter2.js').EventEmitter2;
exports.issue1 = function (test) {
var e = new EventEmitter2();
e.on('foo.bar.baz', function (event, value) {
test.equals('foo.bar.baz', event);
test.equals('somevalue', value);
test.done();
});
e.emit('foo.bar.baz', 'somevalue');
}
exports.issue2 = function (test) {
var e = new EventEmitter2();
e.on('foo.*', function (event, value) {
test.equals('foo.bar.baz', event);
test.equals('somevalue', value);
test.done();
});
e.emit('foo.bar.baz', 'somevalue');
}
// output
/*
$ nodeunit basic.js
basic.js
โ issue1
AssertionError: 'foo.bar.baz' == [ 'foo', 'bar', 'baz' ]
at Object.equals (/Users/kadir/node/lib/node_modules/nodeunit/lib/types.js:81:39)
at EventEmitter2.<anonymous> (/Users/kadir/EventEmitter2/test/basic.js:10:10)
at EventEmitter2.emit (/Users/kadir/EventEmitter2/EventEmitter2.js:94:20)
at /Users/kadir/EventEmitter2/test/basic.js:15:5
at Object.runTest (/Users/kadir/node/lib/node_modules/nodeunit/lib/core.js:54:9)
at /Users/kadir/node/lib/node_modules/nodeunit/lib/core.js:90:21
at /Users/kadir/node/lib/node_modules/nodeunit/deps/async.js:508:13
at /Users/kadir/node/lib/node_modules/nodeunit/deps/async.js:118:13
at /Users/kadir/node/lib/node_modules/nodeunit/deps/async.js:134:9
at /Users/kadir/node/lib/node_modules/nodeunit/deps/async.js:507:9
FAILURES: Undone tests (or their setups/teardowns):
- issue2
To fix this, make sure all tests call test.done()
*/
Hey Paolo
I'd love to do a kind of pipe
to another EventEmitter for all events that haven't been handled by my component.
The API I have in mind is the following:
server.on('foo.*', function(value1, value2) {
console.log(this.event, value1, value2);
}).on('bar.baz', function(value1, value2) {
console.log(this.event, value1, value2);
}).unhandled(function(value1, value2) {
// Any event not in 'foo.*' or 'bar.baz':
console.log(this.event, value1, value2);
});
unhandled
could be onOther
or whatever else fits into the EventEmitter style.
What do you think, could this be low-level enough to be useful for everyone else?
EventEmitter2 should emit removeListener
and removeAllListeners
events when listeners are removed.
Downstream projects like hook.io implement this (see https://github.com/hookio/hook.io/blob/master/lib/hookio/hook.js ). But it should be done here.
needs updating to show new features.
Instantiating a CoffeeScript class defined by new class extends EventEmitter2
throws
TypeError: Cannot read property 'constructor' of undefined
I am aware that utilising pure prototypal inheritance is a goal of this library. However, is there a way this approach can co-exist with CoffeeScript inheritance?
For your reference, the following JavaScript is the compilation of new class extends EventEmitter2
var __hasProp = Object.prototype.hasOwnProperty, __extends = function(child, parent) {
for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; }
function ctor() { this.constructor = child; }
ctor.prototype = parent.prototype;
child.prototype = new ctor;
child.__super__ = parent.prototype;
return child;
};
new ((function() {
__extends(_Class, EventEmitter2);
function _Class() {
_Class.__super__.constructor.apply(this, arguments);
}
return _Class;
})());
Instead of always calling this.emit
from EventEmitter.prototype.on
and making the check for this.newListener
in the emit method, wouldn't it be better just to make a conditional call to this.emit
from EventEmitter.prototype.on
only if this.newListener
is true?. Eg, replacing line 356 by:
if (this.newListener === true) {
this.emit('newListener', type, listener);
}
Maybe I'm missing something, but the current behaviour may be strange for someone extending EventEmitter2 and the emit method, as I was doing :-)
Regards.
Hello,
What do you think of not calling event listeners sequentially in the same JavaScript event processing thread using apply in a for loop? E.g. https://github.com/hij1nx/EventEmitter2/blob/master/EventEmitter2.js#L90
The possible downside of the current approach is that if the first listener throws an exception, the other listeners won't be called, which sucks. I can imagine eventEmitters being used in apps where different listeners originate from different sources/authorities and thus it doesn't makes sense to punish listeners that work correctly for the mischief of a single misbehaving listener.
Perhaps a better solution would be to invoke each listener separately by wrapping listeners in closures and invoking them separately using setTimeout(listener, 0)? That way - if a single listener throws, the others are safe since they will be processed as a separate JS timout event.
Just thinking out loud...
Thanks!
some local variables are actually globals (this actually introduces a bug).
style needs to be corrected.
should have some more comments.
In the following test emit() should return false
var EventEmitter = require('eventemitter2').EventEmitter2;
var ee = new EventEmitter({wildcard: true});
ee.on("foo", function() {
console.log("handled");
});
console.log("emit returned " + ee.emit("bar"));
This can be fixed by changing line 312 of eventemitter2.js
to return listeners.length > 0;
Setup:
var server = new EventEmitter2({
delimiter: '.',
wildcard: true
});
server.on('foo.*', function () {
console.log('foo.*');
});
server.on('foo.bar', function () {
console.log('foo.bar');
});
server.emit('foo.bar');
Expected (in the console):
foo.*
foo.bar
Actual:
foo.bar
foo.*
I expect the listener for foo.*
to be executed before foo.bar
because it is added first.
Is this by design, or is this a bug? You can also see a live version here: http://jsbin.com/IsUJEB/1/edit.
Thank you!
Hi!
From docu:
server.on('foo.*', function(value1, value2) {
console.log(this.event, value1, value2);
});
Am I right that i can't supply bound function as handler, since this
is used to carry event stuff. Wouldn't it be better to not rely on this
and don't constrain use cases? Event per se can be passed as the first parameter.
TIA,
--Vladimir
setting 0 as setMaxListeners always gets overridden by the default value, since assignment is like this:
var m = this._events.maxListeners || defaultMaxListeners;
and 0 is considered falsy.
since the check that controls wether to show a warning or not is m > 0, -1 is a valid argument to setMaxListeners to prevent warnings, since it isn't considered falsy, but this isn't reflected in the documentation
not really sure I understand what this method would do, but it's in the documentation and not in the code
Would it make sense to add emitter.before
and emitter.after
so that we can bind methods to these aspects of events?
The API would look like:
emitter.on('sup::dog', function(){
console.log('hello');
});
emitter.before('sup::dog', function(args){
// indicates that 'sup::dog' event will not fire
return false;
});
emitter.on('sup::cat', function(){
console.log('hello');
// indicates after event will not fire if bound
return false;
});
emitter.after('sup::cat', function(){
console.log('hello');
});
If wildcard option is set to true a newListener event doesn't fire.
Here's a test:
var EventEmitter2 = require('../lib/node_modules/eventemitter2').EventEmitter2;
var x = new EventEmitter2({ wildcard: false });
x.on('newListener', function(){
console.log('this line appears');
});
x.on('test', function(){ });
var y = new EventEmitter2({ wildcard: true });
y.on('newListener', function(){
console.log('this line should appear too, but it\'s not');
});
y.on('test', function(){ });
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.