DEV Community

Cover image for Node.js animated: Event Loop
Andrew Hu for Node Doctors

Posted on Edited on

Node.js animated: Event Loop

We have all heard about JavaScript and Node.js being single-threaded, but what does it mean in practical terms?

It means that JavaScript can do one thing at a time. For example, we cannot simultaneously multiply and sum numbers. We usually do operations in sequence. We add and then multiply or vice versa. Modern computers are fast, and the result of two or more consecutive tasks seems to be computed simultaneously, but there are exceptions.

We all have tried to scrape data from that slow website or waited more than thirty seconds before getting the result of a database query. Do we want to block our single thread from executing more tasks because of a slow database query? Luckily, Node.js doesn’t stop from running other operations because of Libuv, a C++ library responsible for the event loop and asynchronously handling tasks such as network requests, DNS resolution, file system operations, data encryption, etc.

What happens under the hood when Node.js works on tasks such as database queries? We will explore it by following this piece of code step by step.

Code sample to showcase the event loop

The V8 JavaScript engine manages a call stack, an essential piece that tracks which part of our program is running. Whenever we invoke a JavaScript function, it gets pushed to the call stack. Once the function reaches its end or a return statement, it is popped off the stack.

In our example, the line of code console.log('Starting Node.js') is added to the call stack and prints Starting Node.js to the console. By doing so, it reaches the end of the log function and is removed from the call stack.

Function invocation on Node.js call stack

The following line of code is a database query. These tasks are immediately popped off because they may take a long time. They are passed to Libuv, which asynchronously handles them in the background. At the same time, Node.js can keep running other code without blocking its single thread.

In the future, Node.js will know what to do with the query because we have associated a callback function with instructions to handle the task result or error. In our case, it is a simple console.log, but it could be complex business logic or data processing in production applications.

Libuv handles I/O ops

While Libuv handles the query in the background, our JavaScript is not blocked and can continue with console.log(”Before query result”).

Processing I/O while Node.js runs our code

When the query is done, its callback is pushed to the I/O Event Queue to be run shortly*.* The event loop connects the queue with the call stack. It checks if the latter is empty and moves the first queue item for execution.

The event loop checks for an empty call stack

The code is available at https://github.com/fabrilallo/event-loop-1

Pop quiz on the event loop

Try to figure out what the following code prints on the console.

A more complicated code sample to showcase the event loop

An animated guide for the event loop

Conclusion

The event loop, the delegation, and the asynchronous processing mechanism are Node.js's secret ingredients to process thousands of connections, read/write gigantic files, handling timers while working on other parts of our code.

In the article, we saw the vital role of Libuv and its ability to handle numerous potentially long-running tasks. At the same time, we went through the event loop and its role as a bridge/connector between callbacks of asynchronous operations in the I/O event queue and the call stack. In the following articles, we will explore in greater detail how timers, I/O, promises, and ticks are handled by the different phases of the event loop.

If you liked the article, follow us on Twitter @fabriziolallo and @andrewhu368

Top comments (51)

ðŸ”Ĩ āļ‚āđˆāļēāļ§āļ”āđˆāļ§āļ™: nodedoctors/an animated guide to nodejs event loop g - āļŠāļĢāļļāļ›āļ”āļĢāļēāļĄāđˆāļēāļĨāđˆāļēāļŠāļļāļ”
 
thawkin3 profile image
Tyler Hawkins •

For anyone interested, I gave a conference talk on this a couple months ago: https://www.youtube.com/watch?v=KKM_4-uQpow&ab_channel=UtahJS

It's "A Deep Dive into the Node.js Event Loop", complete with tons of code examples so you can see these concepts in action. Here's the GitHub repo as well: github.com/thawkin3/nodejs-event-l...

ðŸ”Ĩ āļ‚āđˆāļēāļ§āļ”āđˆāļ§āļ™: nodedoctors/an animated guide to nodejs event loop g - āļŠāļĢāļļāļ›āļ”āļĢāļēāļĄāđˆāļēāļĨāđˆāļēāļŠāļļāļ”
 
ariannexux profile image
Bento Julio •

Woh Tyler, Your explanation about the topic is one of the best i ever heard, You helped me put together two important concepts that are the task queue and the phases, Thank you Tyler.

ðŸ”Ĩ āļ‚āđˆāļēāļ§āļ”āđˆāļ§āļ™: nodedoctors/an animated guide to nodejs event loop g - āļŠāļĢāļļāļ›āļ”āļĢāļēāļĄāđˆāļēāļĨāđˆāļēāļŠāļļāļ”
 
thawkin3 profile image
Tyler Hawkins •

Thanks Bento! That's very kind of you.

ðŸ”Ĩ āļ‚āđˆāļēāļ§āļ”āđˆāļ§āļ™: nodedoctors/an animated guide to nodejs event loop g - āļŠāļĢāļļāļ›āļ”āļĢāļēāļĄāđˆāļēāļĨāđˆāļēāļŠāļļāļ”
 
debajyotix profile image
Debajyoti Majumder •

Amazing Talk. Thank you Tyler.

ðŸ”Ĩ āļ‚āđˆāļēāļ§āļ”āđˆāļ§āļ™: nodedoctors/an animated guide to nodejs event loop g - āļŠāļĢāļļāļ›āļ”āļĢāļēāļĄāđˆāļēāļĨāđˆāļēāļŠāļļāļ”
 
thawkin3 profile image
Tyler Hawkins •

Thanks Debajyoti! Glad you liked it.

ðŸ”Ĩ āļ‚āđˆāļēāļ§āļ”āđˆāļ§āļ™: nodedoctors/an animated guide to nodejs event loop g - āļŠāļĢāļļāļ›āļ”āļĢāļēāļĄāđˆāļēāļĨāđˆāļēāļŠāļļāļ”
 
yosef_aweke_4537bcc49cd90 profile image
Yosef Aweke •

it is great explanation. thanks man

ðŸ”Ĩ āļ‚āđˆāļēāļ§āļ”āđˆāļ§āļ™: nodedoctors/an animated guide to nodejs event loop g - āļŠāļĢāļļāļ›āļ”āļĢāļēāļĄāđˆāļēāļĨāđˆāļēāļŠāļļāļ”
 
deepak22448 profile image
Deepak Sharma •

How does nodejs know which task should be handled by libuv ?

ðŸ”Ĩ āļ‚āđˆāļēāļ§āļ”āđˆāļ§āļ™: nodedoctors/an animated guide to nodejs event loop g - āļŠāļĢāļļāļ›āļ”āļĢāļēāļĄāđˆāļēāļĨāđˆāļēāļŠāļļāļ”
 
andrewhu368 profile image
Andrew Hu Node Doctors • • Edited

Not an exhaustive list:

  • I/O requests (network requests, file system operations, etc.)
  • setTimeout, setImmediate, setInterval,
  • close callbacks (e.g. socket.on('close', () => {})

Redacted on 8/11/2023

ðŸ”Ĩ āļ‚āđˆāļēāļ§āļ”āđˆāļ§āļ™: nodedoctors/an animated guide to nodejs event loop g - āļŠāļĢāļļāļ›āļ”āļĢāļēāļĄāđˆāļēāļĨāđˆāļēāļŠāļļāļ”
 
tmlr profile image
Tony Miller •

It's not even close to how it actually works. There's no "event queue" (or in other words there are multiple and not exactly queues). "the event loop" has no relation to Node, it's libuv thing, the only relation is that libuv was extracted from early version of Node, process.nextTick never reaches "the event loop" (the one in libuv), Node doesn't run anything in it's single thread while libuv is busy with the query. If it is a single query and there are not timers and setImmediate-s then the whole thing will be blocked on epoll in linux case waiting on that query to return. If there are timeouts then it'll poll that epoll with timeout of 0 and move onto timeouts.

Go to libuv, look at file operations API and compare it to Node's file operation API.

ðŸ”Ĩ āļ‚āđˆāļēāļ§āļ”āđˆāļ§āļ™: nodedoctors/an animated guide to nodejs event loop g - āļŠāļĢāļļāļ›āļ”āļĢāļēāļĄāđˆāļēāļĨāđˆāļēāļŠāļļāļ” Thread
 
tmlr profile image
Tony Miller •

For demonstration purposes I wrote a very simple and primitive http server using raw libuv - github.com/tnymlr/hello-libuv/blob...

Node does very similar thing with the difference that it's running V8 in those handlers.

You JS code runs in libuv handlers with the exception of the entry file, the entry file runs before the event loop has even started.

ðŸ”Ĩ āļ‚āđˆāļēāļ§āļ”āđˆāļ§āļ™: nodedoctors/an animated guide to nodejs event loop g - āļŠāļĢāļļāļ›āļ”āļĢāļēāļĄāđˆāļēāļĨāđˆāļēāļŠāļļāļ” Thread
 
andrewhu368 profile image
Andrew Hu Node Doctors • • Edited

Thank you for your valuable feedback. It is a simplified version of this complex mechanism and we couldn't capture all the details as you mentioned. Would you like to connect on Twitter to improve the animations?

ðŸ”Ĩ āļ‚āđˆāļēāļ§āļ”āđˆāļ§āļ™: nodedoctors/an animated guide to nodejs event loop g - āļŠāļĢāļļāļ›āļ”āļĢāļēāļĄāđˆāļēāļĨāđˆāļēāļŠāļļāļ”
 
deepak22448 profile image
Deepak Sharma •

Thanks sir.

ðŸ”Ĩ āļ‚āđˆāļēāļ§āļ”āđˆāļ§āļ™: nodedoctors/an animated guide to nodejs event loop g - āļŠāļĢāļļāļ›āļ”āļĢāļēāļĄāđˆāļēāļĨāđˆāļēāļŠāļļāļ”
 
hcminhit profile image
i love Math •

hi the last image in this article is being broken, could you please fix it!

ðŸ”Ĩ āļ‚āđˆāļēāļ§āļ”āđˆāļ§āļ™: nodedoctors/an animated guide to nodejs event loop g - āļŠāļĢāļļāļ›āļ”āļĢāļēāļĄāđˆāļēāļĨāđˆāļēāļŠāļļāļ”
 
andrewhalych profile image
Andrew Halych •

Does it mean that Promises, timeouts and intervals are handled differently under the hood in browser and nodejs?

ðŸ”Ĩ āļ‚āđˆāļēāļ§āļ”āđˆāļ§āļ™: nodedoctors/an animated guide to nodejs event loop g - āļŠāļĢāļļāļ›āļ”āļĢāļēāļĄāđˆāļēāļĨāđˆāļēāļŠāļļāļ” Thread
 
andrewhu368 profile image
Andrew Hu Node Doctors •

Chrome uses libevent for the event loop, but promises, timeouts, and intervals are processed in the same order in the two environments.

ðŸ”Ĩ āļ‚āđˆāļēāļ§āļ”āđˆāļ§āļ™: nodedoctors/an animated guide to nodejs event loop g - āļŠāļĢāļļāļ›āļ”āļĢāļēāļĄāđˆāļēāļĨāđˆāļēāļŠāļļāļ” Thread
 
adderek profile image
Maciej Wakuła •

Some things are available in node but not in browser. Ex.setImmediate. This shows that there are differences (even though there are many similarities).

ðŸ”Ĩ āļ‚āđˆāļēāļ§āļ”āđˆāļ§āļ™: nodedoctors/an animated guide to nodejs event loop g - āļŠāļĢāļļāļ›āļ”āļĢāļēāļĄāđˆāļēāļĨāđˆāļēāļŠāļļāļ”
 
animeshsri98 profile image
Animesh Srivastava • • Edited

As you know a task can be either asynchronous (non blocking) or synchronous (blocking) so for any task of asynchronous type nodejs pushes it to be handled by libuv and continue executing other synchronous tasks.

ðŸ”Ĩ āļ‚āđˆāļēāļ§āļ”āđˆāļ§āļ™: nodedoctors/an animated guide to nodejs event loop g - āļŠāļĢāļļāļ›āļ”āļĢāļēāļĄāđˆāļēāļĨāđˆāļēāļŠāļļāļ”
 
aydafield22 profile image
aydafield22 •

Is it true? I hear in some where, asynchronous is talk to non-blocking operations and vice versa synchronous is blocking operations or I'm wrong.

ðŸ”Ĩ āļ‚āđˆāļēāļ§āļ”āđˆāļ§āļ™: nodedoctors/an animated guide to nodejs event loop g - āļŠāļĢāļļāļ›āļ”āļĢāļēāļĄāđˆāļēāļĨāđˆāļēāļŠāļļāļ” Thread
 
animeshsri98 profile image
Animesh Srivastava •

Yes you are correct..updated the comment.

ðŸ”Ĩ āļ‚āđˆāļēāļ§āļ”āđˆāļ§āļ™: nodedoctors/an animated guide to nodejs event loop g - āļŠāļĢāļļāļ›āļ”āļĢāļēāļĄāđˆāļēāļĨāđˆāļēāļŠāļļāļ”
 
madza profile image
Madza •

Great article, learned a few things 👍âœĻðŸ’Ŋ

ðŸ”Ĩ āļ‚āđˆāļēāļ§āļ”āđˆāļ§āļ™: nodedoctors/an animated guide to nodejs event loop g - āļŠāļĢāļļāļ›āļ”āļĢāļēāļĄāđˆāļēāļĨāđˆāļēāļŠāļļāļ”
 
rimla1 profile image
Almir Muminovic •

Does Node works like this:

I've learned how javascript works in background, with example of GEC (Global Execution Context),

Call stack is populated with GEC:
1) console.log("Starting Node js"),
2) db.query()...
3) console.log("Before query results")
Call stack is populated with GEC (this means we cannot handle async code) till we reached console.log("Before query results").

In this animation call stack is populated line by line, and it's empty after each line, for example if we have setTimeout function with 0ms instead of query as 2nd line, even it's async operation it would execute first, before second console.log because call stack is empty after each line.

ðŸ”Ĩ āļ‚āđˆāļēāļ§āļ”āđˆāļ§āļ™: nodedoctors/an animated guide to nodejs event loop g - āļŠāļĢāļļāļ›āļ”āļĢāļēāļĄāđˆāļēāļĨāđˆāļēāļŠāļļāļ”
 
bobbyiliev profile image
Bobby •

Well done!

ðŸ”Ĩ āļ‚āđˆāļēāļ§āļ”āđˆāļ§āļ™: nodedoctors/an animated guide to nodejs event loop g - āļŠāļĢāļļāļ›āļ”āļĢāļēāļĄāđˆāļēāļĨāđˆāļēāļŠāļļāļ”
 
dhruvjoshi9 profile image
Dhruv Joshi •

Helpful and interesting to read! Thanks!

ðŸ”Ĩ āļ‚āđˆāļēāļ§āļ”āđˆāļ§āļ™: nodedoctors/an animated guide to nodejs event loop g - āļŠāļĢāļļāļ›āļ”āļĢāļēāļĄāđˆāļēāļĨāđˆāļēāļŠāļļāļ”
 
juberjj profile image
Juber Nunes •

Very well explained, rich in details. Thanks @fabriziolallo and @andrewhu368

ðŸ”Ĩ āļ‚āđˆāļēāļ§āļ”āđˆāļ§āļ™: nodedoctors/an animated guide to nodejs event loop g - āļŠāļĢāļļāļ›āļ”āļĢāļēāļĄāđˆāļēāļĨāđˆāļēāļŠāļļāļ”
 
peterwitham profile image
Peter Witham •

Thank you for explaining this, it helped me understand far more than other explanations in the past. Appreciate it.

ðŸ”Ĩ āļ‚āđˆāļēāļ§āļ”āđˆāļ§āļ™: nodedoctors/an animated guide to nodejs event loop g - āļŠāļĢāļļāļ›āļ”āļĢāļēāļĄāđˆāļēāļĨāđˆāļēāļŠāļļāļ”
 
ashm10 profile image
Asther Marie Moreno •

Thanks a bunch!!! ðŸŦķðŸŧ

ðŸ”Ĩ āļ‚āđˆāļēāļ§āļ”āđˆāļ§āļ™: nodedoctors/an animated guide to nodejs event loop g - āļŠāļĢāļļāļ›āļ”āļĢāļēāļĄāđˆāļēāļĨāđˆāļēāļŠāļļāļ”
 
guscarpim profile image
Gustavo Scarpim •

Nice!