The most advanced EventEmitter for Node and the browser
MIT License
The most advanced EventEmitter for Node and the browser.
*
and **
)*
and **
)*
and **
)emit
and receive asynchronousnpm install hubjs
Use Browserify to create a standalone file.
var Hub = require('hubjs').Hub;
var hub = new Hub();
Hub.js is an extended implementation of the publish-subscribe pattern.
A subscription is made like this:
hub.on('event', function () {
console.log('Oh, hi!');
});
All listeners registered for 'event'
can be invoked using emit
:
hub.emit('event');
Anything following the event name is used as arguments:
hub.on('log', function (message) {
console.log(message);
});
hub.emit('log', 'Oh, hi!');
When subscribing to an event, the event name may contain glob style wildcards.
The following example matches the events routes.get
and routes.post
:
hub.on('routes.*', function () { /* ... */ });
However, routes.*
does not match routes.get.extended
. The dot acts as a
separator between event name parts. To match multiple event name parts, use a
double wildcard, e.g. routes.**
.
When emitting an event, the event name may contain glob style wildcards. Any filter and listener matching the given event will be invoked. This also applies to whildcard subscriptions.
hub.emit('**.destroy');
Listeners may return a value to the publisher, either by using the return
keyword, or by using a callback.
hub.on('answer', function () {
return 42;
});
To use a callback, simply declare one:
hub.on('answer', function (callback) {
callback(null, 42); // Node style callbacks (err, value)
});
Both of the above examples will work with this publisher:
hub.emit('answer', function (err, value) {
assert.equal(value, 42);
});
This makes it possible to change listener implementations from synchronous to asynchronous without changing the publishers.
By default, the return value of the last invoked listener is returned. To obtain an array with all return values, invoke emit like this:
hub.emit({ event : 'answer', allResults : true }, function (err, values) {
assert.equal(values[0], 42);
});
Filters are special functions that get invoked before the listeners. A filter may delay or prevent listener execution, modify arguments and return values, and add / remove listeners. If an event triggers multiple filters, they form a queue.
A filter function is invoked with two arguments:
next
: a function that must be invoked by the filter to continue processingcallback
: a callback function that must be invoked to return from the callA pass-through filter can be implemented like this:
hub.addFilter('event', function (next) {
// ...
next();
});
Performing custom operations after an event was processed:
hub.addFilter('event', function (next, callback) {
next(function (err, values) {
// ...
callback(err, values);
});
});
to pervent any further filters from being applied and to skip listener execution, invoke the callback directly:
hub.addFilter('event', function (next, callback) {
callback(null, [42]);
});
Note that the callback values MUST ALWAYS be an array.
Filters and listeners are invoked with the same scope object with these properties:
this.event
: The emitted eventthis.args
: The arguments passed to emit, without event and callbackthis.allResults
: Whether the event was emitted with allResults
set totrue
The scope object can be changed in two ways:
emit
, that object will beevent
property and a scope
property. The givenThere are two ways to indicate an error condition:
Both cases are handled in the same way and in this order:
emit
, the callback is invoked withError handling with a callback:
hub.emit('something.throws', function (err) {
if (err) {
console.log('Something went wrong: ' + err);
}
// ...
});
The error event is a "catch all" handler that will cause the hub instance to never throw:
hub.on('error', function (err) {
console.log('Something went wrong: ' + err);
});
In case we do not pass a callback to emit
and we don't have the above
error handler installed, the error will be thrown by emit:
try {
hub.emit('something.throws');
} catch (err) {
console.log('Something went wrong: ' + err);
}
Caveat: If the error happens asynchronously, emit will not throw. The error will be throw globally with no way of handling it properly.
Each hub instance emits these events on event handler registration / deregistraion.
NOTE: In contrast to events emitted with emit
, these events are not
passed to wildcard subscribers. That is, if a filter or a listener was added
with an event name that contains a wildcard, the filter or listener will not be
invoked.
Calling addListener
, on
or once
triggers a newListener
event passing
the event name and the listener function as arguments. If a filter on this
event does not invoke next
, the listener will not be registered.
Calling removeListener
triggers a removeListener
event passing the event
name and the listener function as arguments. If a filter on this event does not
invoke next
, the listener will not be removed.
Calling addFilter
or filterOnce
triggers a newFilter
event passing the
event name and the filter function as arguments. If a filter on this event
does not invoke next
, the filter will not be registered.
Calling removeFilter
triggers a removeFilter
event passing the event name
and the filter function as arguments. If a filter on this event does not
invoke next
, the filter will not be removed.
Hub.js guarantees a predictable call order. The order is as follows:
While listeners are called in registration order, filters are called in reverse
registration order. The call order of "wildcard subscriptions" for listeners
and filters depends on where the wildcards are used: More generic listeners are
called before more specific ones. E.g. if a.b.c
is emitted, a listener on
a.**
is invoked before a listener on a.b.*
.
For more information on wildcard priorities, see the glob-tree match expressions documentation.
Inherits from async-glob-events.AsyncEmitter
and has the glob-filter
API mixed in.
emit(event, ...)
: Invokes the filter chain for the given event beforeremoveAll([event])
: Unregisters all filters and all listeners, or theremoveAll('*')
will remove listeners registered for'*'
, but it will not remove listeners registered for 'event'
.npm install
to install the dev dependenciesnpm test
to lint, run tests on Node and PhantomJS and check code coverageMIT