Javascript – Event-driven vs. ‘reference’-driven programming (i.e. in JavaScript)

eventjavascriptreferencescope

I haven't been able to find the appropriate terminology to search for content on the web related to what I'm asking, so I'm hoping someone on here can at least point me in the right direction.

I'm a fairly senior JavaScript developer, but I have a secret — I don't like event-driven programming (aside from user-triggered event binding I mean). Something about it has never sat well with me, it's the feeling of a "disconnect" in the logic flow.

In the simple example of some module invoking arbitrary numbers of views, an event-driven friend would create event listeners for elements those views could have and keep any reference to the parent module out of the view code.

I, on the other hand, would pass reference to the parent module in to the views so that I could directly trigger the module's methods.

I go for the second approach because I know exactly what is going to happen and what method I'm triggering at all points in the code. In the event-driven case I'm separating the assignment of an event response from the element that would trigger it, putting things in to the 'listening ether'.

I know responsible event-driven programming should avoid any risks like double-binding, etc., but even when that care is taken I still see it all as a bit of a black-box ripe for debugging nightmares. While passing references may be a bit more cumbersome in the code, at least it's clear what things are going to happen and when.

Can anyone identify the pros and cons of these two methods, and in particular why JS seems to be pushing in general toward the event-driven stuff?

Best Answer

Not sure what you mean with reference driven programming. From what I gather, you're wondering what the advantages of event-driven programming are as opposed to writing code, and then using a bunch of branches to determine when to call a given method.

Before I set off, allow me to be pedantic and point out that: a module can't listen for any event, nor can a button have a reference to a module. The button is part of the DOM, the module is JS. Both live in separate universes, and never the twain shall meet.
You can have the event loop pick up on certain events, and then invoke a given function object (either stand-alone, anonymous, or a function object that is part of a module) to handle that event, or you can mix JS into your markup, using certain html attributes (onclick="" and the like).
The latter method is generally said to be outdated, messy, and therefore bad practice. In this day and age, where OOP-buzzwords have found there way into the daily vocab of the average middle-manager, mixing JS in with markup isn't what I'd call separation of concern...

I see it like this:
JavaScript was originally intended to run client side. It's not been considered a valid server-side language for that long. It's small, light and portable, and was meant to make it easy to "liven up" your website. Making the UI responsive, move stuff around and, most importantly, load content dynamically using AJAX.
When talking about AJAX, I think you'll agree, event-driven programming is the better option: you request content, based on user input (clicks links, scrolls the page down etc...), and as you probably know: an ajax response is dealt with using an event handler (xhr.onreadystatechange = function).

Since then, we've come a long way, but some things have remained the same: JS is, in essence, a functional language, which implies closures, lambda's and callback functions to be used all over the place. The easiest way to make functional code work is to set all the functions up, and then let a change in the state (aka event) call one or more of these functions. Basically: functional languages feel at home in an event driven universe.
The expressive power of these constructs is huge, and so it stands to reason that with every new trick, technology or feature that gets added, the people developing the JS engines will tend to implement events to better support these new bells and whistles. Couple that to the fact that the main libs/toolkits will probably provide an API that requires you to pass around functions, and you end up with a bunch of developers who, when confronted with a problem, will look at it as a series of events, that they can tap into to respond accordingly.
So, I guess you might say that JS was designed to be event-driven to a rather large extent, and that the people who use it are "trained" if you will to think accordingly.

Another thing to consider is: how the engines work. V8, for example, is great at idling. If you run node.js, you'll see that it's perfectly happy, consuming very little resources, just sitting there, doing nothing. Once it receives a request (which is an event in a way), it'll wake up and set to work.
Event driven code does reflect this:

http.createServer(function(){}).listen();

This script doesn't do anything, until the server "has to be there", then the function passed to createServer is invoked, and, you could say, becomes the server.

Back to the client-side:
You say you see the point of event binding with DOM elements, and responding to the user's actions. Well, that's what client-side JS does. Its main purpouse was (and is) to augment the user experience. Whatever code gets executed client side, should be triggered by an event.
The event could be the load event, or a click or change event... it doesn't matter. If the user isn't doing anything, JS shouldn't be busy. Some browsers kill a script if it's busy for too long anyway.
You ought to realize that picking up on changes, without using the event loop JS already has to offer, leaves you no real alternative than to write a busy loop: JS isn't meant to be a system language, so you could end up with: for(;;) or while(1) loops. If you then were to have a single event handler, owing to JS being single threaded, that handler can't ever get called, because the JS thread is always busy, claiming the CPU like there's no tomorrow.

I can't see how else you could set about your business, to be honest. Worker's? No, because they communicate only through events. setTimout and setInterval? Not really: it's a right hasstle to sort out the scope, it's error prone and expensive. Besides, an event probably translates to an interrupt at some lower level, as does an interval, or a timeout. What those do is, essentially the same thing as events, only: they're obtrusive, because they weren't initiated by the user, and they could block the UI.
The only "alternative" I can think of, and reading your question and comments again is what you're actually thinking of, is something along the following lines. The first is the reference driven example, the second is a quick translation of the same basic concept to event-driven programming:

var btnMod = (function(w)
{
    var nextOn = false,
    stopEvent = function(e)
    {
        e.returnValue = false;
        e.cancelBubble = true;
        if (e.preventDefault)
        {
            e.preventDefault();
            e.stopPropagation();
        }
        return false;
    },
    module = {};
    module.switch = function(e,elem)
    {
        e = e || w.event;
        //do stuff
        nextOn = !nextOn;//toggle bool
    };
    module.next = function(e,elem)
    {
        e = e || w.event;
        //do stuff
        if (nextOn) location.href = 'next-url';//for example
        else return stopEvent(e);
    };
    return module;
}(window));

then either have markup like below, or bind the module methods to certain elements

<li onclick="module.switch(event, this)">on/off</li>
<li onclick="module.next(event,this)">next</li>

As opposed to doing something like

var btnMod = (function(w)
{
    var stopEvent = function(e)
    {
        e.returnValue = false;
        e.cancelBubble = true;
        if (e.preventDefault)
        {
            e.preventDefault();
            e.stopPropagation();
        }
        return false;
    },
    module = {};
    module.switch = function(e,elem)
    {
        e = e || w.event;
        w.removeEventListener('click', module.switch, false);
        w.addEventListener('click', module.next, false);
    };
    module.next = function(e,elem)
    {
        e = e || w.event;
        w.removeEventListener('click', module.next, false);//unbind to disable
        //do stuff
    };
    if (!nextOn) w.addEventListener('click', module.switch,false);
    else w.addEventListener('click', module.next, false);
}(window));

Well, quite apart from the fact that event-driven code is lighter (only one listener is bound at a time), and less error prone (if the module.next listener is bound, the switch listener isn't), it also doesn't require JS to mixed in with JS. Given some more effort, it can also be written a lot shorter than the snippets I've posted here, and it's not littered with tons of if's and else's. it uses functions, which is what you do in a functional language

Bottom line:
The advantages of event-driven JS are simple, IMO: JS was intended to be used on the web, client-side. It was meant to enhance the user experience by making static pages responsive, and fetching content on-the-fly. All through events. Coupled with the functional paradigm, that makes for a rather expressive little language. To use it in any other way means using it in a way it wasn't designed to be used (excel).

If you don't use a hammer to fasten a screw if you have a screwdriver lying right next to it.
Likewise: you don't work around events, if there is an event readily available.

In the end, though, what's in a name?. If ever you write a standalone program in C, using the GTK+ lib, you'll use "events", too. C# is popular nowadays, guess what, it wont take many tutorials before you see the word "event" crop up. You want to react on some changes in state or user input? That's what we happen to call an event. Lastly: as svidgen pointed out: JS engines already have an event system written out. Good programmers are lazy: they don't go and write their own systems, if they can find an existing one that allows them to do what they need to do. Sure, sometimes you may find the existing systems lacking in some respect, and curse the people making it for not seeing that there might be a need for whatever you happen to need at that time, but that's life.
Personally, I'd say: if you don't like event-driven programming, you don't really like JS(?)

Related Topic