Fixing Events in Javascript
By Zef Hemel
- 3 minutes read - 638 wordsEvents are an integral part of Javascript programming. No matter if you use Javascript in the browser or on the server with node.js, you will be listening to, and perhaps trigger numerous events. Yesterday I talked about Twitterâs Flight, which, I argued, might be pushing events too far. Nevertheless, the fact that something can be pushed too far, does not mean it should avoided altogether. Events are still a good solution to many problems. However, in its current implementation I see a big problem that way too few people complain about: its proneness to silent failure.
In case you never experienced it: from a debugging perspective, silent failure is the worst kind of failure.
I appreciate that Javascript is a dynamic language. If your code calls a method that does not exist, you cannot expect a Javascript interpreter to complain about it as soon as it parsed the source code. The moment that Javascript does complain is when the method call actually happens. This is the nature of dynamic languages.
But what if you attempt to listen to an event that doesnât exist? Or what if you dispatch an event and mistype its name?
Crickets.
âWell,â you may say âwhy would you ever do that?â Well, you wouldnât, not on purpose. But maybe youâre confusing event names, or you simply mistyped the name of the event. It can happen to the best of us. It certainly happened to me a few times.
As an example, the following is perfectly legal Javascript:
var bodyEl = document.getElementsByTagName(âbodyâ)[0];
 bodyEl.addEventListener(âclikâ, function() {
 console.log(âDonât touch my body!â);
 });
So, now Iâm listening to the âclikâ event. More likely than not, this event doesnât exist, but do I get an error when I add the listener? Nope. Do I get a warning at least? Nope.
The same goes for dispatching an event:
this.dispatchEvent(âupdatâ, changes);
Â
 // Some place else in your code
 obj.addEventListener(âupdateâ, updateView);
Do you get an error? No. Do you get a warning? No.
As it turns out, encoding âspecialâ values as strings is a pretty bad idea. So, what would be a safer solution?
Once more, Dart may be a potential source of inspiration. How does Dart implement events?
In Dart, rather than having a single addEventListener
or on
method on an event emitting object, each event is part of the objectâs on
property of type EventListenerList. You can add
a callback to this EventListenerList
, to be called when a given event occurs. For instance, to listen to click events on the body DOM element:
bodyEl.on.click.add((event) {
 console.log(âClicked!â);
 });
If youâd misspell click
as clik
in this scenario, youâd get a exception along the lines of âbodyEl.on does not have a property âclikâ.ââââif Dartâs IDE doesnât give you a warning in-line already. Alternatively, every event can also be used as a Stream, which allows you to do all kinds of fancy things like filtering and other LINQ-like stuff.
As simple as Dartâs solution may be, itâs likely too different from the way events work in Javascript today. So itâs not a viable solution.
A simpler fix is to introduce a .declareEvent
method on event emitters, and enforcing that every event is declared, before it can be dispatched or listened to:
this.declareEvent(âupdateâ);
 this.declareEvent(âerrorâ);
Now, when somebody tries to listen to an event that is not explicitly declared, it would just throw an exception. When you try to dispatch an event that is not declared: exception. Yes, blowing up in your face is better than silent failure, people.
As a added bonus, these event declarations are very valuable documentation. Itâs not uncommon that I have to use an object thatâs an event emitter, but its implementation doesnât clearly advertise the events I can expect it to trigger. If all events were explicitly declared, this information would be so much easier to find.