visit
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 (an easy way to quickly deploy apps) to see it all in action.
The Node.js event loop's order of operations (Source: )
As you can see, there are in the Node.js event loop. Let's briefly look at what happens in each phase:Timers: callbacks scheduled by
setTimeout
and setInterval
are executed during this phasePending callbacks: I/O callbacks that were previously deferred to the next loop iteration are executed during this phase
Idle, prepare: this phase is only used internally by Node.js
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)Check: callbacks scheduled by
setImmediate
are executed during this phaseClose callbacks: close callbacks, like when a socket connection is destroyed, are executed during this phase
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 demo app
Let's look at some examples to better understand the order of operations in the Node.js event loop.
Example 1 - synchronous code
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
.
Next, we'll introduce the concept of
setTimeout
with our second example:Example 2 -
setTimeout
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?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.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.
Next up, we'll introduce the concept of
setImmediate
with our third example:Example 3 -
setImmediate
vs. setTimeout
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
?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.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
.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, , 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.
As a quick sanity check, let's run one more example using
setImmediate
and setTimeout
.Example 4 -
setImmediate
versus setTimeout
againIn 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.Are you starting to get the hang of it?
Let's look at one last example. This time we'll introduce another method called
process.nextTick
.Example 5 -
process.nextTick
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
.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.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, . Confusing, right?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
.
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.If you want to see these examples live in action again, you can always or . You can even .Thanks for reading!