Now that you've learned about Node.js modules, it's time to put this knowledge to work in building a simple Node.js web application. In this chapter, we'll keep to a simple application, enabling us to explore three different application frameworks for Node.js. In later chapters, we'll build some more complex applications, but before we can walk, we must learn to crawl.
We will cover the following topics in this chapter:
EventEmitters
are one of the core idioms of Node.js. Many of the core modules are EventEmitters, and also EventEmitters make an excellent skeleton to implement asynchronous programming. EventEmitters have nothing to do with web application development, but they are so much part of the woodwork that you may skip over their existence. It is with the HTTP classes that we will get our first taste of EventEmitters.
In this chapter, we'll work with the HTTPServer
and HTTPClient
objects. Both of them are subclasses of EventEmitter
and rely on it to send events for each step of the HTTP protocol. Understanding EventEmitter
will help you understand not only these objects but many other objects in Node.js.
The EventEmitter
object is defined in the events module of Node.js. Directly using the EventEmitter
class means performing require('events')
. In most cases, you won't require this module, but there are cases where the program needs to implement a subclass of EventEmitter
.
Create a file named pulser.js
that will contain the following code. It shows both sending as well as receiving events while directly using the EventEmitter
class:
var events = require('events'); var util = require('util'); // Define the Pulser object function Pulser() { events.EventEmitter.call(this); } util.inherits(Pulser, events.EventEmitter); Pulser.prototype.start = function() { setInterval(() => { util.log('>>>> pulse'); this.emit('pulse'); util.log('<<<< pulse'); }, 1000); };
This defines a Pulser
class, which inherits from EventEmitter
(using util.inherits
). Its purpose is to send timed events, once a second, to any listeners. The start
method uses setInterval
to kick off repeated callback execution, scheduled for every second, and call emit
to send the pulse
events to any listeners.
This much could be the starting point of a general-purpose module to send events at a given interval. Instead, we're just exploring how EventEmitter
works.
Now, let's see how to use the Pulser
object by adding the following code to pulser.js
:
// Instantiate a Pulser object var pulser = new Pulser(); // Handler function pulser.on('pulse', () => { util.log('pulse received'); }); // Start it pulsing pulser.start();
Here we create a Pulser
object and consume its pulse
events. Calling pulser.on('pulse')
sets up connections for the pulse
events to invoke the callback function. It then calls the start
method to get the process going.
Enter this into a file and name the file pulser.js
. When you run it, you should see the following output:
$ node pulser.js 19 Apr 16:58:04 - >>>> pulse 19 Apr 16:58:04 - pulse received 19 Apr 16:58:04 - <<<< pulse 19 Apr 16:58:05 - >>>> pulse 19 Apr 16:58:05 - pulse received 19 Apr 16:58:05 - <<<< pulse 19 Apr 16:58:06 - >>>> pulse 19 Apr 16:58:06 - pulse received 19 Apr 16:58:06 - <<<< pulse
The EventEmitter
event has many names, and it can be anything that makes sense to you. You can define as many event names as you like. Event names are defined simply by calling .emit
with the event name. There's nothing formal to do, no registry of event names. Simply making a call to .emit
is enough to define an event name.
An object sends events using the .emit
function. Events are sent to any listeners that have registered to receive events from the object. The program registers to receive an event by calling that object's .on
method, giving the event name and an event handler function.
Often, it is required to send data along with an event. To do so, simply add the data as arguments to the .emit
call, as follows:
this.emit('eventName', data1, data2, ..);
When the program receives that event, the data appears as arguments to the callback function. Your program would listen to such an event as follows:
emitter.on('eventName', (data1, data2, ..) => { // act on event });
There is no handshaking between event receivers and the event sender. That is, the event sender simply goes on with its business, and it gets no notifications about any events received, any action taken, or any error occurred.