@swatk6/emitter
v1.1.0
Published
A drop-in replacement for EventEmitter that can work with cancelable event objects, and a base Emitter class that implements a parent/child architecture and event tree traversal.
Downloads
2
Maintainers
Readme
@swatk6/emitter
A drop-in replacement for EventEmitter that can handle DOM-style events whose propagation can be stopped.
This module exports two classes: emitter
and event
. The emitter
can be used as a basis for extend
ing your own class, and the event
class can be used by itself or be extended to serve your needs for an in-transit cancelable event.
From version 1.1.0 onwards, the emitter
class also comes with a parent-child architecture that allows for event traversal along the hierarchy tree.
Usage
npm install --save @swatk6/emitter
const myemitter = require('@swatk6/emitter').emitter;
const myevent = require('@swatk6/emitter').event;
class MyEmitterClass extends myemitter {
constructor() {
super();
}
}
For a detailed example, see test.js in this package.
Reference
Event traversal
event
objects are emitted byemitter
objects.emitter
objects can be chained up in a parent/children relationship, and thus forming a propagation tree.emitter
objects can be set up to emit events locally, sideways (siblings), upwards (parents) and downwards (children). The path and order of emitting is configurable.event
objects can be set up to be either allowed or disallowed to travel in certain directions.event
objects can also be set up to "saturate" the emitter tree.- Non-saturating events can only be emitted locally, and can travel sideways, stright up the parental chain, and downwards to all children,
relative to the
emitter
they were emitted at. When in non-saturating mode, when anemitter
in the propagation chain is called to emit anevent
, it will only do so locally. - "Saturating" events will be fired on all connected
emitter
s on a tree, regardless of direct lineage. The order of emission is:
- first the event is emitted on the
emitter
object where it was fired at (the origin emitter); - then it is emitted on the siblings of the origin;
- then on all of the children of the origin (recursively down to the last items in the tree);
- then on all of the children of the origin's siblings (recursively, of course);
- then it is emitted on the direct parent of the origin;
- then on all of the siblings of the direct parent of origin;
- then on all the children of the siblings of the direct parent of origin, recursively, down to all of their children;
- then recursively upward on the parent of the direct parent of the origin, and those parents' siblings as well, and so on.
- An
event
's propagation can be stopped at any time.event.stopPropagation()
will allow the event to finish up calling listeners on the currentemitter
before canceling the event propagation to otheremitter
s.event.stopImmediatePropagation()
will cancel the propagation right away. event.eventPhase
reflects on which propagation phase the event is currently in. In the case of "saturating" events, it is either _LOCAL or _SATURATING.event.target
is optionally filled by whatever object is creating the event object.event.currentTarget
is always updated by theemitter
that currently emits the event. There is noevent.deepPath
as I'm afraid it might lead to circular reference hell.*
- However, if you think it'd be a good idea, voice your opinion in the Issues of this package on GitHub.
emitter
Static properties
defaultFirstDirection
= event.PROPAGATES_LOCAL
defaultSecondDirection
= event.PROPAGATES_DOWN
defaultThirdDirection
= event.PROPAGATES_UP
defaultThirdDirection
= event.PROPAGATES_NONE
If you use the parent/child hierarchy extension, these constants set up the default behaviour of all created emitter objects.
This setting describes a setup where the emitted eventObject
first propagates locally (to the listeners of the emitter instance),
then to the emitter object's siblings if any, then to the emitter object's children, if any (where they are propagated locally), and their children, recursively. Once that
propagation is done, the same eventObject
then is propagated to the original emitter's parent where it once again propagates
locally, then, if it is allowed, proceeds upwards to the parent's parent, recursively.
If all four directions are traversed, an event only finishes propagating when:
- all local-level listeners are called;
- and all children of the local emitter are called, and theirs, recursively, until the deepermost children are emitted;
- and the whole of the local emitter's parental chain is called;
- or the event is specified either not to traverse certain directions, or is cancelled by one of the listeners.
Bear in mind, that event traversal at the moment* is a sync operation, therefore it may block your entire process if written carelessly.
defaultMaxListeners
= 0
This is left in only for compatibility reasons, it does not have any effect on the classes' operations.
*: Yes, that means I'm working on an async event emission solution, but there's no ETA on that yet.
As a drop-in-replacement for NodeJS's EventEmitter
This class provides a drop-in replacement functionality for Node's official EventEmitter
class. It's API-compatible for the most part, but it has a few subtle differences.
- It does not care about maximum listener counts (it's just an excuse to tolerate bad programming).
- The class internals look decidedly different from the official
EventEmitter
implementation, so any hacks building on interals abuse will not work. - This class does not optimize for single listeners (which are done without an
Array
in the official implementation), so it might lose a few cycles when compared to the original.** - Neither does it optimize for single or multiple arguments; the algorithm I used for extracting arguments is one that avoids that famed V8 slowdown bug.**
- It supports an object-based (
.event
) event approach that allows for payload and result distribution among listeners (allowing the use of events for dynamically configurable filters and data modifiers) and cancelable events (that stop any subsequent listeners to be called).
**: If you experience significant slowdowns because of these optimization omissions, let me know in an issue and I'll reintroduce them to the codeline.
Methods
addListener(eventName,listener)
on(eventName,listener)
- eventName
<String>
: the event name for which to register this listener - listener
<Function>
: a function that will be called - returns the emitter object itself, allowing for chaining
emit(eventName[,payload1[,payload2...]]):<Boolean>
emit(eventObject):<Boolean>
- eventName
<String>
: the event name for which to register this listener - eventObject
<event>
: an event object that holds all necessary data - returns false if there were no handlers, true otherwise
eventNames():<Array>
- returns an array of all the event names that have handlers
getMaxListeners():0
- a stub; this emitter does not support maximum numbers of listeners
listenerCount(eventName):<Number>
- eventName
<String>
: the event name - returns the number of listeners registered for
eventName
listeners(eventName):<Array>
- eventName
<String>
: the event name - returns an array of listener functions (both normal and one-off) registered for
eventName
off(eventName[,listener])
removeListener(eventName[,listener])
- eventName
<String>
: the event name from which to deregister this listener - listener
<Function>
: a function that will be called; if not given, all listeners foreventName
are removed - returns the emitter object itself, allowing for chaining
once(eventName,listener)
- eventName
<String>
: the event name for which to register this listener - listener
<Function>
: a function that will be called only once - returns the emitter object itself, allowing for chaining
prependListener(eventName,listener)
- eventName
<String>
: the event name for which to register this listener - listener
<Function>
: a function that will be called - like
addListener()
, only it pusheslistener
to the front of the listeners - returns the emitter object itself, allowing for chaining
prependOnceListener(eventName,listener)
- eventName
<String>
: the event name for which to register this listener - listener
<Function>
: a function that will be called only once - like
addListener()
, only it pusheslistener
to the front of the one-time listeners - returns the emitter object itself, allowing for chaining
removeAllListeners([eventName])
- eventName
<String>
: if given, onlyeventName
listeners will be expunged - returns the emitter object itself, allowing for chaining
setMaxListeners(number)
- a stub; this emitter does not support maximum numbers of listeners
Events
newListener(eventName,listener)
- eventName
<String>
: the event name for which this listener is about to be registered - listener
<Function>
: a function that will be called
removeListener(eventName,listener)
- eventName
<String>
: the event name for which this listener is about to be unregistered - listener
<Function>
: a function that will be called
As a hierarchy-aware (parent/child relations) advanced event emitter
addChild(childEmitter)
- childEmitter
<emitter>
: the emitter instance to add as a child - returns the emitter object itself, allowing for chaining
callOnChildren(method[,params[,thisarg[,childArray]]])
- method
<String>
: the method name to call on children emitters - params
<Array>
: an array of method call arguments, presented as an Array, defaults to [] - thisarg
<Function|Object>
: the this argument for the method call, defaults tothis
- childArray
<Array>
: if only a selected list of children should be triggered, this can be a filtered list - returns the result value from the chain of method calls
Important: no matter how many params
are given, there will always be an extra argument,
and that is the result of the previous child's method call. In the first called child, it will be null
.
callOnParent(method[,params[,thisarg]])
- method
<String>
: the method name to call on children emitters - params
<Array>
: an array of method call arguments, presented as an Array, defaults to [] - thisarg
<Function|Object>
: the this argument for the method call, defaults tothis
- returns either the result of the method call or
false
if there is no parent defined
callOnSiblings(method[,params[,thisarg]])
- method
<String>
: the method name to call on children emitters - params
<Array>
: an array of method call arguments, presented as an Array, defaults to [] - thisarg
<Function|Object>
: the this argument for the method call, defaults tothis
- returns the result value from the chain of method calls
emitEvent(eventObject[,skipDirection])
- eventObject
<event>
: an event object to emit - skipDirection
<Number>
: one of thePROPAGATES_*
constants from the event class, or 0 for no boundaries. - returns the emitter object itself, allowing for chaining
Please be advised that using emitEvent()
turns off the compatibility mode for event.stopImmediatePropagation()
, and also event.phase
is
set according to the current propagation phase.
filterChildren(filterFunction):<Array>
- filterFunction
<Function>
: a callback that is executed to filter the children emitters of the current one; internally,Array.prototype.filter()
is used - returns a filtered child array that can be fed as the last argument for
callOnChildren()
to selectively call methods on current children
getAllChildren([dontDescend])
- dontDescend an optional
<Array>
of<emitter>
objects that the function should not descend to - returns an
<Array>
of<emitter>
objects which are registered as children of the current emitter or its children (recursively down to the last level)
getChildren()
- returns an
<Array>
of<emitter>
objects which are registered as children of the current emitter
getParent()
- returns either
null
or a<emitter>
which is a parent to the current emitter
getRelations([dontDescend])
- dontDescend an optional
<Array>
of<emitter>
objects that the function should not descend to (only used in recursive calls) - returns an
<Array>
of<emitter>
objects which are related to the currentemitter
(are in the same propagation tree), regardless of how removed they are
getSiblings()
- returns an
<Array>
of<emitter>
s that share the same parent as this emitter
hasChild(childEmitter):<Boolean>
- childEmitter
<emitter>
: an emitter or derivative - returns true if *childEmitter is a direct descendant of the current emitter; false otherwise
hasParent():<Boolean>
- returns true if this emitter has a parent
removeAllChildren()
- returns the emitter object itself, allowing for chaining
Removes all children of the current emitter instance.
removeChild(childObject|index)
- returns the emitter object itself, allowing for chaining
Removes a specified childObject
, or the emitter
that resides within the children at position held in index
.
removeSelf()
- returns the emitter object itself, allowing for chaining
Removes the current emitter instance as a child from its parent. This is here instead of a removeParent()
or similar.
setOrder(first,second,third,fourth)
- first : the first route to take; one of the
PROPAGATES_*
constants from the event class, or 0 for "do nothing" - second : the second route to take; --""--
- third : the third route to take; --""--
- fourth : the fourth route to take; --""--
- returns the emitter object itself, allowing for chaining
If the event comes with a getPropagation()
that contains PROPAGATES_SATURATING
, then the order is discarded.
shutdown()
- returns the emitter object itself, allowing for chaining
Safely shut down this emitter, by letting go of foreign references; this allows for easier cleanup.
event
This class can be used on its own as a basis for a generic event object, or it can be extended to suit your needs.
Constants
PROPAGATES_NONE
= 0
meaning no propagation
PROPAGATES_LOCAL
= 1
meaning only current emitter object
PROPAGATES_UP
= 2
meaning only current emitter's parent
PROPAGATES_DOWN
= 4
meaning only current emitter's children
PROPAGATES_SIBLINGS
= 8
meaning only current emitter's siblings
PROPAGATES_SATURATING
= 16
meaning the entire emitter tree
These constants can be OR
ed, so for example a PROPAGATES_UP
|PROPAGATES_LOCAL
defines an event or an emitter that spreads events locally and up.
PROPAGATES_SATURATING
is a special case which ignores the rest of the constants.
Methods
new event(eventName[,payload[,origin]])
- eventName
<String>
: the event's name (or type, YMMV) - payload
<any>
: the event's payload, optional, defaults tonull
- origin
<Function|Object>
: the function or object that created the event, optional, defaults tonull
Creates a new event object (constructor).
canPropagate([where])
- where
<Number>
:- if nothing is given (no arguments), returns true if this event is allowed to propagate at all (not canceled);
- if one of the
event
constants are given, then returns true if the event can propagate in that direction.
- returns
<Boolean>
cancelEvent()
stopPropagation()
- returns the event object itself, allowing for chaining
In compatibility mode (mainline 1.0.x), which is the default, these cancel the event, so they're equal to stopImmediatePropagation()
.
In native mode (if the event is emitted with emitter.emitEvent()
), these only cancel upward or downward propagation, leaving the local level intact.
In both modes, stops event propagation even on the same level -- ie. no more listeners are called.
getPayload()
setPayload(newValue)
getResult()
setResult(newValue)
- newvValue
<any>
- set* methods return the event object itself, allowing for chaining
Accessor methods for the event's built-in payload and result containers.
getPropagation()
setPropagation(newValue)
- newValue
<Number>
: an ORed list ofPROPAGATES_*
constants which tells the emitter which directions are allowed for propagation in this event - setPropagation() returns the event object itself
Setting this and emitter.setOrder()
will jointly decide how an event can propagate. In extreme situations, like where the emitter is set up to emit
only locally and upwards, and the event set up so that it would only propagates downwards, no event is actually emitted even though there are no
stop*Propagation() calls and the emitter.emitEvent()
is called.
stopImmediatePropagation()
- returns the event object itself, allowing for chaining
Static properties
event.defaultPropagation
: <Number>
The value (an ORed list of PROPAGATES_*
constants) which newly created event objects use as their setPropagation() defaults.
Properties
bubbles
: <Boolean>
If true, the event can traverse outside its local boundary.
currentTarget
: <emitter>
The current emitter that is emitting this event object.
eventPhase
: <Number>
One of the PROPAGATES_*
constants, depending on current traversal status. In "saturating" propagation,
it's set to PROPAGATES_LOCAL
only at the start, then for the rest of propagation it's PROPAGATES_SATURATING
.
timeStamp
: <Number>
The millisecond-precise epochal time when the event object came to be.
target
: <Function|Object>
The event's origin function or object, as set by the third argument of the constructor, defaults to null
.
type
: <String>
Contains the event name/type.