When you head to the node.js website. You would see that they define Node.js as an asynchronous event-driven javascript runtime. You can understand an event as some single that indicates that some action occurred.
Node.js offers us a module named events . Using which we can create or listen to an event and perform actions for those events. There are multiple methods that the events module provide us . The two major though are
- on
Now, this method is a listener it takes in a string name. This name defines the name of the action that you want to set the listener for. - emit
As already discussed above emit simply means that an action has just triggered. the string value you pass to this method is the name of the action that got triggered.
Lets try understanding the above concept with a simple piece of code
var events = require('events')
const EventEmitter = new events()
EventEmitter.on('SomeEvent',_=> console.log("it works!!!"))
EventEmitter.emit('SomeEvent')
When you run the above piece of code. You receive the below output.
it works!!!
Let’s understand the simple piece of code above.
We require the library events which is provided to us by the node.js environment. Now, we created an instance of our events class as all objects that can emit events are instance of this class. This object will allow us to use the method such as to emit event and to listen events among many others.
Next up we set up a listener. That listens for an event named SomeEvent. When this SomeEvent triggers the registered callback functions run too. Over here we simply log to the console a simple message. Next, We fire this SomeEvent event using the emit method. By providing it the value SomeEvent we are telling that we want to trigger this event. When this event is emitted. We already have an event emitter registered that listens for this very event. On receiving this event it performs whatever method was registered. In our case, it simply logs out to the screen the message.
it works!!!
Now you can pass arguments too with the emit method and capture those arguments in the callback of your listener. Let’s take an example as told you can take multiple arguments In our example here we are just taking a single parameter an object.
var events = require('events')
const EventEmitter = new events()
EventEmitter.on('clicked',obj=> console.log(`${obj.username} aged ${obj.age} performed a click action`))
EventEmitter.emit('clicked',{username:"X", age:32})
When we run the above code. We receive the following output.
X aged 32 performed a click action
Other methods provided by the event module.
We have seen the on method which simply listens for the event everytime that event is triggered and it performs the desired operation on that event every time. But what if you only wanted to perform or listen for the action only when it was called the first time. In such cases the events module provides you with the method called once.
Let’s Understand this with the same example if you duplicate the last emit line in the above example. You will see that our on method listened for our event both the time and action was performed accordingly. But if you change the on method to once no matter how many times you emit the message. It will only listen once. the first time it was called.
var events = require('events')
const EventEmitter = new events()
EventEmitter.once('clicked',obj=> console.log(`${obj.username} aged ${obj.age} performed a click action`))
EventEmitter.emit('clicked',{username:"Y", age:64})
EventEmitter.emit('clicked',{username:"X", age:32})
When you run the code above. you will see that the output is
Y aged 64 performed a click action
Which corresponds to the value in our first emit. It means that we only listened for the event click only once. That too on the first occurrence every other time the event occurred we ignored that event and no subsequent action(In our case printing the values to console) was performed.
Now one thing to notice is that event emitter should be singleton. That means only one instance of the the object should be used to emit and listen the events. if you emit an event from a different instance than the one you are listening to then it won’t work. Take the example below.
var events = require('events')
const EventEmitter = new events()
const EventEmitter2 = new events()
EventEmitter.once('clicked',obj=> console.log(`${obj.username} aged ${obj.age} performed a click action`))
EventEmitter2.emit('clicked',{username:"Y", age:64})
if you run the code above it won’t do anything as the instance that emit the event and the one that listens to the event are two different instances. So always keep in mind that both the emitter and the listener should be of the same instance.
Other Important Functions in the Event Emitter
The eventNames() method :
The eventNames() returns the name of all the active listeners attached to the EventEmitter instance.
var events = require('events')
const EventEmitter = new events()
EventEmitter.once('Done',obj=> console.log(`Done`))
EventEmitter.once('clicked',obj=> console.log(`${obj.username} aged ${obj.age} performed a click action`))
EventEmitter.once('searched',obj=> console.log(`searched`))
console.log(EventEmitter.eventNames());
The output to above function is an array that contains the name of all the listeners. Running the above code you get the following output
[ ‘Done’, ‘clicked’, ‘searched’ ]
The removeAllListeners([]) method:
To remove all or a particular event listener you could use the removeAllListeners method . You pass the name of the event listeners you wish to remove in an array passed as an argument to this function. Lets take the same example above and use this method to check what is the result.
var events = require('events')
const EventEmitter = new events()
EventEmitter.once('Done',obj=> console.log(`Done`))
EventEmitter.once('clicked',obj=> console.log(`${obj.username} aged ${obj.age} performed a click action`))
EventEmitter.once('searched',obj=> console.log(`searched`))
EventEmitter.removeAllListeners(['Done'])
EventEmitter.emit("Done")
console.log(EventEmitter.eventNames());
So, As you can see we have three listener and we remove the listener named ‘Done‘ . We emit the same event and also try to print out the names of the event listener attached to that event Emitter but as we removed the event Done emitting this event will have no affect at all . It won’t trigger anything. So our eventNames method should returns us all the name barring ‘Done‘. Lets see what is the output.
[ 'clicked', 'searched' ]
The setMaxListeners(n) method
By default, the event emitter will give you a warning if you try to register more than 10 event listeners for a particular event. To Bypass this or lower this value we can use the setMaxListeners method it only accepts one parameter and that is the max listener number. let’s take a small piece of code to understand this.
var events = require('events')
let EventEmitter = new events()
EventEmitter.setMaxListeners(1)
EventEmitter.on('Done',obj=> console.log(`Done`))
EventEmitter.on('Done',obj=> console.log(`Done2`))
EventEmitter.on('clicked',obj=> console.log(`${obj.username} aged ${obj.age} performed a click action`))
EventEmitter.on('searched',obj=> console.log(`searched`))
EventEmitter.emit("Done")
console.log(EventEmitter.eventNames());
Now we set the maxListener to 1 that is we allow want to register maximum of one listener per event. We registered two listener for Done . Now the thing to note here is that even though we set max listener to 1 and then defined two listeners on Done event . It wouldn’t throw an exception that would lead to stopping the execution of the code . Rather it will issue you a warning to let you know that you bypassed an event.
Lets see what output we get when we run the above.
Done
Done2
[ 'Done', 'clicked', 'searched' ]
(node:27709) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 2 Done listeners added. Use emitter.setMaxListeners() to increase limit
As expected we got a warning for the same.
Although we covered all the basics but if you want to dig in deeper. There is nothing better than the official documentation. Link to event emitter topic in the official documentation here.