Learn Node.js with Brigadier Fluffykins Part II: Events, EventEmitter and the Event Loop
Welcome to Part II of Learn Node.js With Brigadier Fluffykins, a series created to help you easily understand Node.js ❤
In Part I Brigadier Fluffykins and I introduced Node.js, what you can build with it, async/sync concepts. I walked you through install, and together we created your first server.
It was glorious:
Today’s lesson will cover:
- Why Node.js is an event driven language, and how this is important for asynchronous behavior
- How events in the DOM are similar to events in Node.js
- How the Event Loop processes requests
- Creating custom events using the EventEmitter
Event Driven Programming is Awesome
Since Node.js is single-threaded, in order for it to create concurrency and not be painfully slow — as the traditional client server model explained in Part I — it uses events to listen for requests.
This is different from say, Apache, a web server that uses multi-threaded HTTP. For every request Apache receives, it creates a new thread for handling it. This means that yes, while you can have more than one process running at the same time via threads, the downfall is that results from all the requests have to come back before serving the page.
On the other hand, Node.j’s event driven architecture allows for multiple requests to be processed on a single thread. For example, once a request event is triggered, callbacks and promises process these requests asynchronously.
This means if you have multiple requests coming in and request A is still doing its thing, request B will start fetching the results — the outcome being either request B responds to the client before request A or at the same time as Request A.
Since everything is processed faster, the client has a better user experience. Lets discuss this in more detail further in the lesson.
There are some downfalls to Node.js’s concurrency model but we’ll be covering this in the next few lessons.
Events in the DOM are like Events in Node.js
Think of events this way: just as events interact with DOM objects, many objects in Node.js emit events.
For example, when you set up a click event, you can have a callback say: “when something is clicked, turn the third div blue!”
Here’s a coded example.
In your index.html file:
In your main.js file:
And, if you want to test this out in your own browser here’s some CSS. This should go in style.css:
When the client clicks the button, our click event is triggered, and our callback does something to the DOM. In this case, it turns the third div blue and changes the text inside the button.
Like the request event in Node.js, when the client clicks a button, it’s as if they are sending a request into the main.js file where the click event is listening — just as the request event would listen in for incoming requests.
Then, just like the response event would respond to the client with some information inside the callback, the callback of the DOM’s click event responds by changing the background color of the third div. It also changes the text in the button inside the html file.
The main difference between events in Node.js and events in the DOM is that DOM events stay primarily attached to the DOM object — on the client-side — while events for Node.js are focused more on the relationship between the client and the server.
Node.js emits events from objects — such as the web server object(http.createServer). Lucky for you, you’ve actually already used events back in Part I in STEP #1.5!
During this step, you saved the web server object in it’s own variable and listened for incoming requests via the request event attached to the http.createServer object in the first parameter.
Underneath this object is the EventEmitter constructor, which we’ll learn about very soon. For now, review the code we set up in Part I and see if you have a better grasp of what’s going after our event explanation.
Here it is again for reference:
The Event Loop
Ok so you have a basic understanding of events and how they relate with Node.js but how does Node.js actually work under the hood?
The first thing Node.js does when it reads your code is subscribe events you used, such as request, listen, connection or close. Once it’s done, it goes into the Event Loop and listens for these events continuously inside a single thread.
For example, in the server we previously created above, it’s only listening for the request event and thus the Event Loop thinks:
“Have any requests come in?”
“How about now?”
No worries, Node.js’s single threaded Event Loop is not Jules Winfield. It’s actually just patiently waiting and listening for events that it previously subscribed to in the background.
If a request does arrive, it triggers the request event and runs the callback we wrote — in our case, the mini html inside the end method in our previous server example. Also be aware that events can trigger other events.
But what if multiple requests come at the same time? Like the request and close event? The event loop will process these events one at time. So first the request event will be processed and then the close event. While they are being handled they don’t block more events from coming in. If they did, our code would run twice as long.
Lets dive further into what this all means
Once the results are retrieved, the first request “pops off,” and only then would the second request go into the Call Stack and get executed:
Lets dive deeper now that we know about the Call Stack.
When an asynchronous function with a callback or event comes into the Call Stack, it automatically moves into the Web API. The Web API is where events subscribed to the Event Loop are stored. They await orders from the Event Loop, which listens if any of the events are called.
Once someone triggers an event, for example, the request event, the callback of this event gets sent into an event queue. This queue is also called the callback queue or just task queue.
Here’s a visual demonstration of everything we just explained:
- The golden bars below are regular, non-asynchronous, functions. The last pink and green bars are two request events. These events are subscribed to Event Loop (played by Brigadier Fluffykins) and wait inside the Web API to be called.
- As the events wait, other functions are executed on the Call Stack.
- Once an event is triggered, the Event Loop hears it and that particular event’s callback moves into the queue. Although, since this is the request event, it would first wait for any results it needs. And only then does it send the callback over into the queue.
- While there are still functions running and being executed on the Call Stack, the events have to wait for the Call Stack to empty in order for them to run. Brigadier Fluffykins lets them know if it’s A-OK to move into the Call Stack or not depending on if it’s empty or not.
Lets Create Custom Events!
Event emitters are used extensively in Node.js libraries, so lets learn how to create our own and better understand how they work!
All objects that emit events are instances of the EventEmitter class and all events inherit from the EventEmitter constructor. We’ll be creating two events for the bunnyError event emitter — bunnyWarning and bunnyNeed.
Copy and paste this into a file called bunnyEmitter.js:
Alright so what’s happening here?
First we require in Node.js’s EventEmitter object and then we create an instance of a new EventEmitter object that we will build custom events for. We call this instance bunnyError.
Then we create an event listener for our first event, bunnyWarning, with the on method, which listens for the event. We handle this event when it’s used by triggering a callback that simply prints “BUNNY WARNING: warning.”
Notice I used Template Literals — an ES6 feature. You can learn more them here. It’s the same as saying console.log(“BUNNY WARNING:” + message).
Lastly we use the emit method to trigger or call the event. Once the event is called the callback should run. We can do this as many times as we want.
Assuming the file is on your Desktop, type node bunnyEmitter.js in your shell:
If you only want your event emitter to trigger one time, the EventEmitter object has a method called .once which you can use instead of .on:
With this, no matter how many times you emit yourEvent, it will only work one time.
It’s good practice to limit the number of event listeners you have. In fact if you have more than ten, you will get a warning:
"(node) warning: possible EventEmitter memory leak detected. 11 listeners added. Use emitter.setMaxListeners() to increase limit."
So far you’ve seen terms such as events, event listener, and event handler being used. Lets make the main differences clear before we move on:
The event listener is the event you create to listen for any incoming events. The event handler is the callback that will be triggered once the event listener hears the event.
In our custom event emitter example, the event listeners were bunnyWarning and bunnyNeeds and the event handlers were the callbacks of each event.
Check out these extra resources
- Node.js docs on events
- List of events for the DOM
- You learned to create an instance of an event emitter but what if you wanted to extend it and use it in different files? Check this tutorial out
- Learn more the methods on the EventEmitter object
- Want to learn more about the Event Loop?
Congrats! You’ve made it through Learn Node.js With Brigadier Fluffykins Part II! In today’s lesson you learned that Node.js is an event driven language and why this is useful for asynchronous behavior. You also learned how these events are processed via the Event Loop.
We also took a dive into learning about the similarities between DOM events and events in Node.js to help you ease into this new realm a bit more.
Lastly, we created out first EventEmitter and two awesome events!
Lets learn more about these topics as well as others we’ve only scratched in the next few lessons. Thank you for reading and stay tuned.
Keep your wisdom up to date by clicking the ❤ below and following, as more Learn Node.js With Brigadier Fluffykins is coming soon to Medium!