Event-driven programming - node.js, Java

980 Views Asked by At

I am coming from Java but have been doing some Node.js lately, and have been looking at the EventEmitter module in Node.

What I don't understand is the fundamental difference between Event-driven programming and 'regular' programming.

Here is some psuedo-code to demonstrate my idea of "event-driven" programming.

EventEmitter ee = new EventEmitter();
Function f = new SpecialFunction();

ee.on('grovel',f);

ee.emit('grovel'); //calls function 'f'

the only work the EventEmitter object seems to be doing is creating a hash relationship between the String representation of an event (in this case 'grovel') and a function to respond with. Seems like that's it, not much magic there.

however, my question is - how does event-driven programming really work behind the scenes with low-level events like mouse-clicks and typing? In other words, how do we take a click and turn it into a string (like 'grovel') in our program?

1

There are 1 best solutions below

0
On

Okay. I will take a run at this.

There are a couple of major reasons to use event emitters.

One of the main reasons is that the browser, which is where JavaScript was born, sometimes forces you to. Whether you are wiring your events straight into your HTML, using jQuery or some other framework/library, or whatever, the underlying code is still basically the same (erm, basically...)

So first, if you want to react to a keyboard or mouse event, like you mentioned, you could just hard bind directly to an event handler (callback) like this:

<div onclick="myFunc(this)">Click me</div>

...or you could do the exact same thing in JS by DOM reference:

document.getElementById('my_element').onclick = function (evt) {
    alert('You clicked me');
};

This used to be the primary way we wired up click handlers. One lethal drawback to this pattern is that you can only attach one callback to each DOM event. If you wanted to have a second callback that reacted to the same event, you would either need to combine write it into the existing click handler or build a delegate function to handle the job of calling the two functions. Plus, your event emitter ends up being tightly coupled to the event listener, and that is generally a bad thing.

As applications became more complex, it makes more sense to use event listeners, instead. Browser vendors (eventually) settled on a single way to do this:

// Build the handler
var myHandler = function (evt) {
    alert('You clicked me too');
    window.myHandlerRef = this; // Watch out! See below.
};

// Bind the handler to the DOM event
document.getElementById('my_element').addEventListener('click', myHandler);

The advantage to this pattern is that you can attach multiple handlers to a single DOM event, or call one single event handler from several different DOM events. The disadvantage is that you have to be careful not to leak: depending on how you write them, event-handling closures (like myHandler above) can continue to exist after the DOM element to which they were attached have been destroyed and GCed. This means it is good practice to always do a removeEventListener('click', myHandler). (Some libraries have an off() method that does the same thing).

This pattern works well for keyboard events as well:

var giveUserAHeadache = function (evt) {
    alert('Annoying, isn\'t it?');
};

document.addEventListener('keypress', giveUserAHeadache);

Okay. So that is how you usually handle native browser events. But developers also like to use this pattern of event delegation in their own code. The reason you would want to do this is so you can decouple your code as much as possible.

For example, in a UI, you could have an event emitted every time the user's browser goes offline (you might watch navigator.onLine for example). Maybe you could have a green/red lamp on your page header to show the online state, and maybe you could disable all submit buttons when offline, and maybe also show a warning message in the page footer. With event listeners/emitters, you could write all of these as completely decoupled modules and they still can work in lock-step. And if you need to refactor your UI, you can remove one component (the lamp, for example), replace it with something else without worrying about screwing up the logic in some other module.

As another example, in a Node app you might want your database code to emit an error condition to a particular controller and also log the error -- and maybe send out an email. You can see how these sorts of things might get added iteratively. With event listeners, this sort of thing is easy to do.

You can either write your own, or you can use whatever pattern is available in your particular environment. jQuery, Angular, Ember and Node all have their particular methods, but you are free to also build your own -- which is what I would encourage you to try.

These are all variations of the same basic idea and there is a LOT of blur over the exact definition or most correct implementation (in fact, some might question if these are different at all). But here are the main culprits:

  1. Observer
  2. Pub-Sub
  3. Mediator