Using Events in Node.js
Tags: javascript, node, webdev, tutorial
Published: 2020-01-05 22:00:57

**Subscribe to my email list now at http://jauyeung.net/subscribe/**

**Follow me on Twitter at https://twitter.com/AuMayeung**

**Many more articles at https://medium.com/@hohanga**

The core feature of Node.js is asynchronous programming. This means that code in Node.js may not be executed sequentially. Therefore, data may not be determined in a fixed amount of time. This means that to get all the data we need, we have to pass data around the app when the data is obtained. This can be done by emitting, listening to, and handling events in a Node.js app. When an event with a given name is emitted, the event can listen to the listener, if the listener is specified to listen to the event with the name. Event emitter functions are called synchronously. The event listener code is a callback function that takes a parameter for the data and handles it. Node.js has an `EventEmitter` class — it can be extended by a new class created to emit events that can be listened to by event listeners.

* * *

Define Event Emitters
=====================

Here’s a simple example of creating and using the `EventEmitter` class:

```javascript
const EventEmitter = require('events');

class Emitter extends EventEmitter {}

const eventEmitter = new Emitter();  
eventEmitter.on('event', () => {  
  console.log('event emitted!');  
});

eventEmitter.emit('event');
```

We should get ‘event emitted!’ in the console log. In the code above, we created the `Emitter` which extends the `EventEmitter` class, which has the `emit` function we called in the last line. The argument of the `emit` function is the name of the event, which we listen to in this block of code:

```javascript
eventEmitter.on('event', () => {  
  console.log('event emitted!');  
});
```

The callback function, after the `'event'` argument above, is the event handler function, that runs when the event is received.

In the code above, we emitted an event. However, it’s not very useful since we didn’t pass any data with the emitted event when we emit the event so it doesn’t do much. Therefore, we want to send data with the event so that we can pass data around so that we can do something useful in the event listener. To pass data when we emit an event, we can pass in extra arguments after the first argument, which is the event name. For instance, we can write the following code:

```javascript
const EventEmitter = require('events');  
class Emitter extends EventEmitter {}  
const eventEmitter = new Emitter();
eventEmitter.on('event', (a, b) => {  
  console.log(a, b);  
});

eventEmitter.emit('event', 'a', 'b');
```

If we run the code above, we get ‘a’ and ‘b’ in the `console.log` statement insider the event handler callback function. As we see, we can pass in multiple arguments with the `emit` function to pass data into event handlers that subscribe to the event. After the first one, the arguments in the `emit` function call are all passed into the event listener’s callback function as parameters, so they can be accessed within the event listener callback function.

We can also access the event emitter object inside the event listener callback function. All we have to do is change the arrow function of the callback to a tradition function, as in this code:

```javascript
const EventEmitter = require('events');  
class Emitter extends EventEmitter {}  
const eventEmitter = new Emitter();
eventEmitter.on('event', function(a, b){  
  console.log(a, b);  
  console.log(`Instance of EventEmitter: ${this instanceof EventEmitter}`);  
  console.log(`Instance of Emitter: ${this instanceof Emitter}`);  
});
eventEmitter.emit('event', 'a', 'b');
```

If we run the code above, we get this logged in the `console.log` statements inside the event listener callback function:

```
a b  
Instance of EventEmitter: true  
Instance of Emitter: true
```

On the other hand, if we have the following:

```javascript
const EventEmitter = require('events');  
class Emitter extends EventEmitter {}  
const eventEmitter = new Emitter();eventEmitter.on('event', (a, b) => {  
  console.log(a, b);  
  console.log(`Instance of EventEmitter: ${this instanceof EventEmitter}`);  
  console.log(`Instance of Emitter: ${this instanceof Emitter}`);  
});
eventEmitter.emit('event', 'a', 'b');
```

Then we get this logged in the `console.log` statements inside the event listener callback function.:

```
a b  
Instance of EventEmitter: false  
Instance of Emitter: false
```

This is because arrow functions do not change the `this` object inside it. However, tradition functions _do_ change the content of the `this` object.

`EventEmitter` calls all listeners synchronously, in the order that they’re registered. This eliminates the chance of race conditions and other logic errors. To handle events asynchronously, we can use the `setImmediate()` or the `process.nextTick()` methods:

```javascript
const EventEmitter = require('events');  
class Emitter extends EventEmitter {}  
const eventEmitter = new Emitter();
eventEmitter.on('event', (a, b) => {  
  setImmediate(() => {  
    console.log('event handled asychronously');  
  });  
});  
eventEmitter.emit('event', 'a', 'b');
```

In the code above, we put the `console.log` inside a callback function of the `setImmediate` function, which will run the event handling code asynchronously instead of synchronously.

Events are handled every time they’re emitted. For example, if we have:

```javascript
const EventEmitter = require('events');  
class Emitter extends EventEmitter {}  
const eventEmitter = new Emitter();  
let x = 1;

eventEmitter.on('event', (a, b) => {  
  console.log(x++);  
});
for (let i = 0; i < 5; i++){  
  eventEmitter.emit('event');  
}
```

Since we emitted the ‘event’ event five times, we get this:

```
1  
2  
3  
4  
5
```

If we want to emit an event and handle it only the first time it’s emitted, then we use the `eventEmitter.once()` function, as in this code:

```javascript
const EventEmitter = require('events');  
class Emitter extends EventEmitter {}  
const eventEmitter = new Emitter();  
let x = 1;

eventEmitter.once('event', (a, b) => {  
  console.log(x++);  
});

for (let i = 0; i < 5; i++){  
  eventEmitter.emit('event');  
}
```

As expected, we only get this logged in the `console.log` statement of the event handler above:

```
1
```

* * *

Error Handling
==============

If an error event is emitted in the case of errors, it’s treated as a special case within Node.js. If the `EventEmitter` doesn’t have at least one error event listener register and an error is emitted, the error is thrown, and the stack trace of the error will be printed, and the process will exit. For example, if we have the following code:

```javascript
const EventEmitter = require('events');  
class Emitter extends EventEmitter {}  
const eventEmitter = new Emitter();
eventEmitter.emit('error', new Error('Error occured'));
```

Then we get something like this and the program exits:

```bash
Error [ERR_UNHANDLED_ERROR]: Unhandled error. (Error: Error occured  
    at evalmachine.<anonymous>:5:28  
    at Script.runInContext (vm.js:133:20)  
    at Object.runInContext (vm.js:311:6)  
    at evaluate (/run_dir/repl.js:133:14)  
    at ReadStream.<anonymous> (/run_dir/repl.js:116:5)  
    at ReadStream.emit (events.js:198:13)  
    at addChunk (_stream_readable.js:288:12)  
    at readableAddChunk (_stream_readable.js:269:11)  
    at ReadStream.Readable.push (_stream_readable.js:224:10)  
    at lazyFs.read (internal/fs/streams.js:181:12))  
    at Emitter.emit (events.js:187:17)  
    at evalmachine.<anonymous>:5:14  
    at Script.runInContext (vm.js:133:20)  
    at Object.runInContext (vm.js:311:6)  
    at evaluate (/run_dir/repl.js:133:14)  
    at ReadStream.<anonymous> (/run_dir/repl.js:116:5)  
    at ReadStream.emit (events.js:198:13)  
    at addChunk (_stream_readable.js:288:12)  
    at readableAddChunk (_stream_readable.js:269:11)  
    at ReadStream.Readable.push (_stream_readable.js:224:10)
```

To prevent the Node.js program from crashing, we can listen to the error event with a new event listener and handle the error gracefully in the error event handler. For example, we can write:

```
const EventEmitter = require('events');  
class Emitter extends EventEmitter {}  
const eventEmitter = new Emitter();
eventEmitter.on('error', (error) => {  
  console.log('Error occurred');  
});
eventEmitter.emit('error', new Error('Error occurred'));
```

Then we get “error occurred” logged. We can also get the error content with the `error` parameter of the event handler callback function. If we log it, as in this code:

```bash
const EventEmitter = require('events');  
class Emitter extends EventEmitter {}  
const eventEmitter = new Emitter();eventEmitter.on('error', (error) => {  
  console.log(error);  
});eventEmitter.emit('error', new Error('Error occurred'));
```

We will get something like this:

```
Error: Error occurred  
    at evalmachine.<anonymous>:7:28  
    at Script.runInContext (vm.js:133:20)  
    at Object.runInContext (vm.js:311:6)  
    at evaluate (/run_dir/repl.js:133:14)  
    at ReadStream.<anonymous> (/run_dir/repl.js:116:5)  
    at ReadStream.emit (events.js:198:13)  
    at addChunk (_stream_readable.js:288:12)  
    at readableAddChunk (_stream_readable.js:269:11)  
    at ReadStream.Readable.push (_stream_readable.js:224:10)  
    at lazyFs.read (internal/fs/streams.js:181:12)
```

* * *

More Ways to Deal with Events
=============================

`Node.js` will emit one special event without writing any code to emit the event: The `newListener` . The `newListener` event is emitted before a listener is added to the internal array of listeners. For example, if we have the following code:

```javascript
const EventEmitter = require('events');  
class Emitter extends EventEmitter {}  
const eventEmitter = new Emitter();  
eventEmitter.on('newListener', (event, listener) => {  
  console.log(event);  
});
```

Then we get something like this logged:

```bash
Emitter {  
  _events: [Object: null prototype] { newListener: [Function] },  
  _eventsCount: 1,  
  _maxListeners: undefined }
```

This happens even when no events are emitted. Whatever is in the handler will be run before the code in event handlers for any other events.

The `removeListener` function can be used to stop event listener functions from listening to events. This takes two arguments: The first is a string that represents the event name, the second is the function that you want to stop using to listen to events. For example, if we want to stop listening to the “event” event with our listener function, then we can write this:

```javascript
const EventEmitter = require('events');  
class Emitter extends EventEmitter { }  
const eventEmitter = new Emitter();
const listener = () => {  
  console.log('listening');  
}

eventEmitter.on('event', listener)setInterval(() => {  
  eventEmitter.emit('event');  
}, 300);

setTimeout(() => {  
  console.log("removing");  
  eventEmitter.removeListener('event', listener);  
}, 2000);
```

Then we get something like this in the output:

```bash
Timeout {  
  _called: false,  
  _idleTimeout: 2000,  
  _idlePrev: [TimersList],  
  _idleNext: [TimersList],  
  _idleStart: 1341,  
  _onTimeout: [Function],  
  _timerArgs: undefined,  
  _repeat: null,  
  _destroyed: false,  
  [Symbol(unrefed)]: false,  
  [Symbol(asyncId)]: 10,  
  [Symbol(triggerId)]: 7 }
listening  
listening  
listening  
listening  
listening  
listening  
removing
```

The event emitter emits the “event” event in the code above once every 300 milliseconds. This is listened to by the `listener` function, until it’s been prevented from listening again by calling the `removeListener` function with the “event” as the event name the `listener` event listener function in the callback of the `setTimeout` function.

Multiple event listeners can register for a single event. By default, the limit for the maximum number of event listeners is ten. We can change this with the `defaultMxListeners` function in the `EventEmitter` class. We can set it to any positive number. If it’s not a positive number, then a `TypeError` is thrown. If more listeners than the limit are registered then a warning will be output. For example, if we run the following code to register 11 event listeners for the “event” event:

```javascript
const EventEmitter = require('events');  
class Emitter extends EventEmitter { }  
const eventEmitter = new Emitter();
const listener = () => {  
  console.log('listening');  
}

for (i = 1; i <= 11; i++){  
  eventEmitter.on('event', listener);  
}

eventEmitter.emit('event');
```

When we run the code above, we get this:

```bash
listening  
listening  
listening  
listening  
listening  
listening  
listening  
listening  
listening  
listening  
listening  
(node:345) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 event listeners added. Use emitter.setMaxListeners() to increase limit
```

However, if we call `setMaxListeners` to set it to `getMaxListeners() + 1`, which is 11 listeners, as seen in the following code:

```javascript
const EventEmitter = require('events');  
class Emitter extends EventEmitter { }  
const eventEmitter = new Emitter();  
eventEmitter.setMaxListeners(eventEmitter.getMaxListeners() + 1);
const listener = () => {  
  console.log('listening');  
}

for (i = 1; i <= 11; i++){  
  eventEmitter.on('event', listener);  
}

eventEmitter.emit('event');
```

Then we get the following logged:

```bash
listening  
listening  
listening  
listening  
listening  
listening  
listening  
listening  
listening  
listening  
listening
```

An important feature of Node.js is asynchronous programming. This means that code in Node.js may not be executed sequentially. Data may not be determined in a fixed amount of time. This means that to get all the data we need, we have to pass data around the app when the data obtained. We can emit events and handle them within a Node.js app.

When an event with a given name is emitted, the event can listen to the listener, if the listener is specified to listen to the event with the name. Event emitter functions are called synchronously. The event listener code is a callback function that takes a parameter for the data and handles it. Node.js has an `EventEmitter` class that can be extended by a new class that we create to emit events that can be listened to by event listeners. With the `EventEmitter` class, we can create new `EventEmitter` classes that can emit and listen to events. We can attach multiple event listeners to one event that can do different things. Also, we can set the maximum number of event listeners to as many as we want. Finally, we can choose to handle events asynchronously instead of synchronously.