Preface
The problem described below might be topical for virtually any event-driven JS framework and any application that processes incoming data/event stream. For the sake of definiteness, let's imagine a web-based IM (Facebook chat-like) using qooxdoo framework.
The application receives incoming event stream (via WebSocket, for example) and re-emits events to its internal classes:
Events are processed, roughly speaking, in two stages. First stage handlers are mostly alerts (play a sound on incoming message, display a web notification etc.) Second stage does actual data processing and display. Both stages handlers are invoked simultaneously as events arrive.
This model provides good code decoupling between the application and the handlers, allowing to add/remove and enable/disable them independently. However...
The Problem
...as the application evolves, turns out there can be dependencies between the stages. Some stage 1 handlers should block those of stage 2 (ex., an incoming voice recording should not autoplay until sound alert has completed). Some might even show a user confirmation, and cancel the whole remaining chain if the confirmation has not been given. The event handling in qooxdoo assumes that all the handlers are invoked (nearly) simultaneously, and there is no control over the order and timing of handler invocations.
How do we introduce the required control, while remaining within the event model and not sacrificing its benefits (low coupling, etc.)?
Solution
The candidate solution employs Promises. By default, qooxdoo event handlers do not return anything. Why not making them (optionally) return a Promise? In this case, a promise-aware event mediator should be introduced:
The handlers now should be subscribed to the mediator (this is omitted from the diagram for the sake of clarity). The mediator, in addition to the standard on
/off
methods, should implement an after
method with the following semantics:
after(String name, Function listener, var ctx?)
- invoke handler after all other handlers for this eventafter(String name, Integer id, Function listener, var ctx?)
- invoke handler after another handler with a known IDafter(String name, (Class|String) id, Function listener, var ctx?)
- invoke handler after all other handlers of some known class (could be derived fromthis
argument of the corresponding call)
Thus, we extend the existing event semantics at two points:
- event handlers may now return Promises;
- an
after
method is introduced for an event emitter/mediator.
The emitter/mediator should resolve dependencies and wire handler invocations to the corresponding then()
blocks of Promises.
The proposed solution seems to satisfy both requirements: 1) it implements dependencies between event handlers, 2) it allows to stay within the event handling paradigm. Are there any pitfalls? Can it be done better/cleaner? Any critique and advice is welcome.