4

Understanding the Node.js Event Loop

 3 years ago
source link: https://hackernoon.com/arthurunderstanding-the-nodejs-event-loop-hin34zp
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Understanding the Node.js Event Loop

@thawkin3Tyler Hawkins

Senior software engineer. Continuous learner. Educator.

JavaScript is single-threaded, so how does it handle asynchronous code without blocking the main thread while it waits for an action to complete? The key to understanding the asynchronous nature of JavaScript is understanding the event loop.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

In the browser, the event loop coordinates the execution of code between the call stack, web APIs, and the callback queue. Node.js, however, implements its own "Node.js event loop," which is different from the regular "JavaScript event loop." How confusing!

0 reactions
heart.png
light.png
money.png
thumbs-down.png

The Node.js event loop follows many of the same patterns as the JavaScript event loop but works slightly differently, as it doesn't interact with the DOM but does deal with things like input and output (I/O).

0 reactions
heart.png
light.png
money.png
thumbs-down.png

In this article, we'll dive into the theory behind the Node.js event loop and then look at a few examples using

setTimeout
,
setImmediate
, and
process.nextTick
. We'll even deploy some working code to Heroku (an easy way to quickly deploy apps) to see it all in action.
0 reactions
heart.png
light.png
money.png
thumbs-down.png
0 reactions
heart.png
light.png
money.png
thumbs-down.png

The Node.js Event Loop

The Node.js event loop coordinates the execution of operations from timers, callbacks, and I/O events. This is how Node.js handles asynchronous behavior while still being single-threaded. Let's look at a diagram of the event loop below to get a better understanding of the order of operations:

0 reactions
heart.png
light.png
money.png
thumbs-down.png

The Node.js event loop's order of operations (Source: Node.js docs)

0 reactions
heart.png
light.png
money.png
thumbs-down.png

As you can see, there are six main phases in the Node.js event loop. Let's briefly look at what happens in each phase:

0 reactions
heart.png
light.png
money.png
thumbs-down.png

Timers: callbacks scheduled by

setTimeout
and
setInterval
are executed during this phase
0 reactions
heart.png
light.png
money.png
thumbs-down.png

Pending callbacks: I/O callbacks that were previously deferred to the next loop iteration are executed during this phase

0 reactions
heart.png
light.png
money.png
thumbs-down.png

Idle, prepare: this phase is only used internally by Node.js

0 reactions
heart.png
light.png
money.png
thumbs-down.png

Poll: new I/O events are retrieved and I/O callbacks are executed during this phase (except for callbacks scheduled by timers, callbacks scheduled by

setImmediate
, and close callbacks, because those are all handled in different phases)
0 reactions
heart.png
light.png
money.png
thumbs-down.png

Check: callbacks scheduled by

setImmediate
are executed during this phase
0 reactions
heart.png
light.png
money.png
thumbs-down.png

Close callbacks: close callbacks, like when a socket connection is destroyed, are executed during this phase

0 reactions
heart.png
light.png
money.png
thumbs-down.png

It's interesting to note that

process.nextTick
isn't mentioned anywhere in any of these phases. That's because it's a special method that's not technically part of the Node.js event loop. Instead, whenever the
process.nextTick
method is called, it places its callbacks into a queue, and those queued callbacks are then "processed after the current operation is completed, regardless of the current phase of the event loop" (Source: Node.js event loop docs).
0 reactions
heart.png
light.png
money.png
thumbs-down.png
0 reactions
heart.png
light.png
money.png
thumbs-down.png

Event Loop Example Scenarios

Now, if you're like me, those explanations of each phase of the Node.js event loop may still seem a little abstract. I learn by seeing and by doing, so I created this demo app on Heroku for running various code snippet examples. In the app, clicking on any of the example buttons sends an API request to the server. The code snippet for the selected example is then executed by Node.js on the backend, and the response is returned to the frontend via the API. You can view the full code on GitHub.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

Node.js event loop demo app

0 reactions
heart.png
light.png
money.png
thumbs-down.png

Let's look at some examples to better understand the order of operations in the Node.js event loop.

0 reactions
heart.png
light.png
money.png
thumbs-down.png
0 reactions
heart.png
light.png
money.png
thumbs-down.png

Example 1

We'll start with an easy one:

0 reactions
heart.png
light.png
money.png
thumbs-down.png

Example 1 - synchronous code

0 reactions
heart.png
light.png
money.png
thumbs-down.png

Here we have three synchronous functions called one after the other. Because these functions are all synchronous, the code is simply executed from top to bottom. So because we call our functions in the order

first
,
second
,
third
, the functions are executed in the same order:
first
,
second
,
third
.
0 reactions
heart.png
light.png
money.png
thumbs-down.png
0 reactions
heart.png
light.png
money.png
thumbs-down.png

Example 2

Next, we'll introduce the concept of

setTimeout
with our second example:
0 reactions
heart.png
light.png
money.png
thumbs-down.png

Example 2 -

setTimeout
0 reactions
heart.png
light.png
money.png
thumbs-down.png

Here we call our

first
function, then schedule our
second
function using
setTimeout
with a delay of 0 milliseconds, then call our
third
function. The functions are executed in this order:
first
,
third
,
second
. Why is that? Why is the
second
function executed last?
0 reactions
heart.png
light.png
money.png
thumbs-down.png

There are a couple key principles to understand here. The first principle is that using the

setTimeout
method and providing a delay value doesn't mean that the callback function will be executed exactly after that number of milliseconds. Rather, that value represents the minimum amount of time that needs to elapse before the callback will be executed.
0 reactions
heart.png
light.png
money.png
thumbs-down.png

The second key principle to understand is that using

setTimeout
schedules the callback to be executed at a later time, which will always be at least during the next iteration of the event loop. So during this first iteration of the event loop, the
first
function was executed, the
second
function was scheduled, and the
third
function was executed. Then, during the second iteration of the event loop, the minimum delay of 0 milliseconds had been reached, so the
second
function was executed during the "timers" phase of this second iteration.
0 reactions
heart.png
light.png
money.png
thumbs-down.png
0 reactions
heart.png
light.png
money.png
thumbs-down.png

Example 3

Next up, we'll introduce the concept of

setImmediate
with our third example:
0 reactions
heart.png
light.png
money.png
thumbs-down.png

Example 3 -

setImmediate
vs.
setTimeout
0 reactions
heart.png
light.png
money.png
thumbs-down.png

In this example, we execute our

first
function, schedule our
second
function using
setTimeout
with a delay of 0 milliseconds, and then schedule our
third
function using
setImmediate
. This example begs the question: Which type of scheduling takes precedence in this scenario?
setTimeout
or
setImmediate
?
0 reactions
heart.png
light.png
money.png
thumbs-down.png

We've already discussed how

setTimeout
works, so we should give a brief background on the
setImmediate
method. The
setImmediate
method executes its callback function during the "check" phase of the next iteration of the event loop. So if
setImmediate
is called during the first iteration of the event loop, its callback method will be scheduled and then will be executed during the second iteration of the event loop.
0 reactions
heart.png
light.png
money.png
thumbs-down.png

As you can see from the output, the functions in this example are executed in this order:

first
,
third
,
second
. So in our case, the callback scheduled by
setImmediate
was executed before the callback scheduled by
setTimeout
.
0 reactions
heart.png
light.png
money.png
thumbs-down.png

It's interesting to note that the behavior you see with

setImmediate
and
setTimeout
may vary depending on the context in which these methods are called. When these methods are called directly from the main module in a Node.js script, the timing depends on the performance of the process, so the callbacks could actually be executed in either order each time you run the script. However, when these methods are called within an I/O cycle, the
setImmediate
callback is always invoked before the
setTimeout
callback. Since we are invoking these methods as part of a response in an API endpoint in our example, our
setImmediate
callback always gets executed before our
setTimeout
callback.
0 reactions
heart.png
light.png
money.png
thumbs-down.png
0 reactions
heart.png
light.png
money.png
thumbs-down.png

Example 4

As a quick sanity check, let's run one more example using

setImmediate
and
setTimeout
.
0 reactions
heart.png
light.png
money.png
thumbs-down.png

Example 4 -

setImmediate
versus
setTimeout
again
0 reactions
heart.png
light.png
money.png
thumbs-down.png

In this example, we schedule our

first
function using
setImmediate
, execute our
second
function, and then schedule our
third
function using
setTimeout
with a delay of 0 milliseconds. As you might have guessed, the functions are executed in this order:
second
,
first
,
third
. This is because the
first
function is scheduled, the
second
function is immediately executed, and then the
third
function is scheduled. During the second iteration of the event loop, the
second
function is executed since it was scheduled by
setImmediate
and we're in an I/O cycle, and then the
third
function is executed now that we're in the second iteration of the event loop and the specified delay of 0 milliseconds has passed.
0 reactions
heart.png
light.png
money.png
thumbs-down.png

Are you starting to get the hang of it?

0 reactions
heart.png
light.png
money.png
thumbs-down.png
0 reactions
heart.png
light.png
money.png
thumbs-down.png

Example 5

Let's look at one last example. This time we'll introduce another method called

process.nextTick
.
0 reactions
heart.png
light.png
money.png
thumbs-down.png

Example 5 -

process.nextTick
0 reactions
heart.png
light.png
money.png
thumbs-down.png

In this example, we schedule our

first
function using
setImmediate
, schedule our
second
function using
process.nextTick
, schedule our
third
function using
setTimeout
with a delay of 0 milliseconds, and then execute our
fourth
function. The functions end up being called in the following order:
fourth
,
second
,
first
,
third
.
0 reactions
heart.png
light.png
money.png
thumbs-down.png

The fact that the

fourth
function was executed first shouldn't be a surprise. This function was called directly without being scheduled by any of our other methods. The
second
function was executed second. This is the one that was scheduled with
process.nextTick
. The
first
function was executed third, followed by the
third
function last, which shouldn't be a surprise to us either since we already know that callbacks scheduled by
setImmediate
get executed before callbacks scheduled by
setTimeout
when inside an I/O cycle.
0 reactions
heart.png
light.png
money.png
thumbs-down.png

So why did the

second
function scheduled by
process.nextTick
get executed before the
first
function scheduled by
setImmediate
? The method names are misleading here! You would think that a callback from
setImmediate
would get executed immediately while a callback from
process.nextTick
would get executed on the next tick of the event loop. However, it's actually the other way around. Confusing, right?
0 reactions
heart.png
light.png
money.png
thumbs-down.png

It turns out that a callback from

process.nextTick
gets executed immediately during the same phase as it was scheduled. A callback from
setImmediate
gets executed during the next iteration or tick of the event loop. So in our example, it makes sense that the
second
function scheduled by
process.nextTick
was executed before the
first
function scheduled by
setImmediate
.
0 reactions
heart.png
light.png
money.png
thumbs-down.png
0 reactions
heart.png
light.png
money.png
thumbs-down.png

Conclusion

By now you should be a little more familiar with the Node.js event loop as well as with methods like

setTimeout
,
setImmediate
, and
process.nextTick
. You can certainly get by without digging into the internals of Node.js and the order of operations in which commands are processed. However, when you begin to understand the Node.js event loop, Node.js becomes a little less of a black box.
0 reactions
heart.png
light.png
money.png
thumbs-down.png

If you want to see these examples live in action again, you can always check out the demo app or view the code on GitHub.  You can even deploy the code to Heroku yourself by clicking here.

0 reactions
heart.png
light.png
money.png
thumbs-down.png

Thanks for reading!

0 reactions
heart.png
light.png
money.png
thumbs-down.png
6
heart.pngheart.pngheart.pngheart.png
light.pnglight.pnglight.pnglight.png
boat.pngboat.pngboat.pngboat.png
money.pngmoney.pngmoney.pngmoney.png
by Tyler Hawkins @thawkin3. Senior software engineer. Continuous learner. Educator.Check out my web portfolio!
Join Hacker Noon

Create your free account to unlock your custom reading experience.


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK