Fixing Events in Javascript

Events are an integral part of Javascript programming. No matter if you use Javascript in the browser or on the server with [node.js](http://nodejs.org), 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](https://zef.me/5390/decoupling-events-vs-dependency-injection). 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](http://darglang.org) 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](http://api.dartlang.org/docs/releases/latest/dart_html/EventListenerList.html). 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](http://api.dartlang.org/docs/releases/latest/dart_async/Stream.html), which allows you to do all kinds of fancy things like filtering and other [LINQ-like](http://msdn.microsoft.com/en-us/library/vstudio/bb397926.aspx) 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.