A javascript triple phase event system
It already seems a long time ago when my favorite framework YUI3 ended lifetime. YUI3 was ahead of time in many ways. Some of its features still seem to be re-invented nowadays.
My favorite part was YUI’s Custom Events. It has a triple phase event system, working throughout the whole framework.
This was the inspiration to create itsa-event: a javascript triple phase event system. No framework, just a plain module that brings in those powerful features. It also supports asynchronicity, through e.returnValue
that can be a Promise with a future value.
You can download itsa-event
on npm.
Introduction
Event systems can be:
- Single phase (after-phase events like nodejs uses)
- Double phased (before-phase and after-phase)
- Triple phased (before-, action- and after-phase)
Itsa-event is a triple phase event system. When events are emitted, they go through 3 phases: before
, action
and an after
phase.
Before phase
When an event is emitted, all before
subscribers will be called in the order in which they subscribed.
The before
-listeners could be used to either:
- Call e.preventDefault()
- Call e.halt()
- Decorate the payload (event object).
Since any before
-subscriber can interrupt the event, before subscribers should not be used get notified of an event. Instead, use it to prevent an event from happening or add information.
Default action
If no before
listener interrupted the event, the default action (defaultFn) gets invoked - if defined. The default action can be defined with .defaultFn()
: see the examples below.
If the defaultFn returns a value, this will be made available by e.returnValue
. This value can be a Promise. The event cycle does not wait for Promises to resolve and stays high performant. The Promise return value can be inspected in the after-listeners by its thenable.
Prevented action
If any before
-listener has interrupted the processing by calling e.preventDefault() on the event object, the preventedFn()
-method will be called instead. Examples below show how to setup a preventedFn.
After phase
Once the event has taken place, the after
listeners will be called. e.halt() and e.preventDefault() are no longer being applicable. after
subscribers could change the event object but that is not recommended. All after
listeners should see the same event object.
Defining Custom Events
By default you first define your custom events. You do not necessarily need to, but without, they don’t have a default function, which is typically what an event is all about: doing something when emitted.
A Custom Event can be set up like this:
The chained functions are optional, but it makes sense to define at least a defaultFn.
emitterName:eventName
Events are identified by their customEvent
name, which has the syntax: emitterName:eventName
.
The emitterName
is usually the entity that emits the event and will be available at the eventobject's property: e.emitter. The eventName
specifies the event of the emitter. It is the second part of the customEvent, after the colon. The eventName
is event object's property: e.type.
Subscribing to events
Subscribing (listening) to events can be done by either the before or after listeners:
Wildcard, emitters and events
Because of the emitterName:eventName
structure, you can subscribe to events in different ways:
- emitter:event -> listen very specific
- emitter:* -> listen to any event of a specific emitter
- *:event -> listen to a specific event, regardless of its emitter
Emitting events
Events can be emitted with Event.emit(customEvent, payload)
:
Interrupting Events
Custom Events can be interrupted by invoking e.preventDefault() or e.halt() inside a before subscriber.
e.halt()
will stop everything: no other before subscribers, defaultFn, preventedFn or after subscribers will be invoked.
e.preventDefault()
will invoke all other before-, but no after subscribers. Also, the preventedFn will be invoked instead of defaultFn, assuming it is defined.