wl - Whenable events
There are two kinds of events:
-
Reusable events which may happen many times, like a mouse click or a keypress. When subscribing to such an event, one normally does not care if an event has already been triggered in the past. One just needs to react to the event each time it happens in the future.
-
One-off events which only happen once, for instance a page load event, an ajax-request responce, a complete of a calculation delegated to a worker, or an asynchronous function callback. For this kind of events it matters if an event has already been triggered at the moment of subscription (which means there should be additional check). In latter case the listener should likely be performed immediately.
Whenable is a design pattern targeted to simplify dealing with the second kind of events by providing a special kind of listener subscriber. When using that subscriber, one does not need to worry about if an event has already been triggered, in this case the listener is invoked immediately. Additionally, the subscriber may be used several times to store additional listeners.
A good example of what could be simplified by using the Whenable solution is the page onload event. Here is how one can listen to the onload event in the traditional style:
if documentreadyState == "complete" // page has already been loaded ; else // preserving any existing listener var origOnload = windowonload || {}; window{ ; ; }
The code should be performed each time a new listener should react to the onload event. Using a whenable subscriber everything above is simplified to:
window;
The code says: call the given function doWhatWeNeed()
if the page
is loaded, otherwise wait until the page is loaded, and then call the
function.
Listener subscribers which behave like explained above are
conventionally named starting with the when..
prefix and followed by
a past participle describing an event: whenLoaded()
,
whenCompleted()
, whenFailedToLoad()
and so on. The Whenable term
is also used to refer to a one-off event supporting this kind of
subscription.
Whenable pattern was inspired by Promises, and it is similar to Promises in that it also allows not to care about when an event actually fires. But unlike Promises, Whenable is much easier to use and understand, produces simplier code, and is more general solution thus covering a wider range of use-cases.
This wl
library implements the Whenable
object, which represents
such an event and can be used to easily produce a whenable-style
subscriber.
Installation
For the web-browser environment — download the
distribution,
unpack it and load the wl.js
in a preferrable way. That is an
UMD module, thus for instance it may simply be loaded as a plain
JavaScript file using the <script>
tag:
For Node.js — install wl
with npm:
$ npm install wl
and then in your code:
var wl = ;
Optionally you may load the script from the distribution:
var wl = ;
After the module is loaded, the wl.Whenable()
constructor is
available.
Usage
Constructing a Whenable event is simple:
var myWhenable = ;
The object has the two methods. The emit()
method fires the event
and invokes the subscribed listeners:
myWhenable;
The getSubscriber()
method returns a whenable-style subscriber
function:
var whenEventTriggered = myWhenable;
The subscriber may later be reused to subscribe a listener to the event:
;
The myListener()
will be invoked after the event is triggered. If
the event has already been triggered at the moment of subscription,
the listener is called immediately (yet asynchronously in order to
keep the flow consistent).
The methods of the Whenable
object (along with the Whenable
instance itself) are not supposed to be exposed to the event
user. Normally the Whenable
event is stored private and is emitted
by internal means. Instead the whenable subscriber function (returned
by the getSubscriber()
method) is to be provided to the user so that
he can attach listeners to the event.
When subscribing a listener, the context may be provided as a second argument:
;
Upon the event is triggered, the subscribed listeners are executed in their respective contexts (if provided upon subscription).
Additionally, the emit()
method may take any set of arguments which
are simply forwarded as the arguments provided to the subscribed
listeners. This allows to supply the listeners with some details about
the event:
myWhenable;
Examples
Here is an ordinary asynchronous function which executes a callback after some time:
var { ;}
Let us create a Whenable event representing the function completion:
var somethingWhenable = ; var { ;} var whenSomethingDone = somethingWhenable;
Now there are the two functions:
-
initiateSomething()
starts the process which should lead to the event emission in the future, and -
whenSomethingDone()
, the whenable-style subscriber which may subscribe as many listeners as needed, before or after the event is emitted.
Those two functions may now be used separately.
Similarly, if there is an asynchronous routine with two outcomes, one may prepare the two whenable events:
var { var { try // do something that may fail ... catche return ; ; } ;}
var success = ;var failure = ; var { ;} var whenSomethingSucceded = success;var whenSomethingFailed = failure;
The code above provides the similar initiator function
initiateSomething()
, and the two whenable subscribers,
whenSomethingSucceeded()
and whenSomethingFailed()
which subscribe
a provided listener to the success or failure outcomes respectively.
Another example: here is the implementation of the magic
window.whenLoaded()
subscriber given in the beginning of this
text. The subscriber is used to react to the page load event:
var onloadWhenable = ; if documentreadyState == "complete" // already loaded onloadWhenable; else // preserving existing listener var origOnload = windowonload || {}; window{ ; onloadWhenable; } windowwhenLoaded = onloadWhenable;