This article is a short follow up to my original article series about Node.js Event Loop. In the original article series, I discussed in detail about Timers, setImmediate, process.nextTick, Promises and many more.
However, since Node.js v11.0.0, there are some significant changes to the behavior of setTimeout, setImmediate, process.nextTick and Promises. In this article, I’m going to discuss these new changes along with some comparisons of the functionality between Node < v11.0.0 and Node ≥ v11.0.0. If you miss any of my previous articles about Node.js Event loop, I recommend you read them from the following links and return here to see the new changes introduced in Node v11.0.0.
- Event Loop and the Big Picture
- Timers, Immediates and Next Ticks
- Promises, Next-Ticks, and Immediates
- Handling I/O
- Event Loop Best Practices
- New changes to timers and microtasks in Node v11 (This article)
Rationale
If you run the following piece of code in the browser and node separately, you’ll get contradicting results.
In the browser, you will get:
timeout1
timeout2
promise resolve
timeout3
timeout4
However, in Node versions below 11.0.0, you’ll get the following output:
timeout1
timeout2
timeout3
timeout4
promise resolve
In Node JS implementation, process.nextTick callbacks and microtasks (e.g, promise callbacks) were executed between each phase of the event loop when the C++/JavaScript boundary is crossed. Therefore, all timer callbacks are executed in the timers phase of the event loop before the Promise callback is executed which resulted in the above output.
However, this contradicting output between the browser and Node has been under discussion for a while and a feature (or a fix) has been landed in Node.js v11.0.0 to follow the browser behavior. With this feature, Node.js v11.0.0 or above will output the following which matches the browser’s output:
timeout1
timeout2
promise resolve
timeout3
timeout4
See the following comparison between Node v10.15.1 and Node v11.10.0:
This change not only affects setTimeout, but also affectssetImmediate. Let’s try to run the following code in Node v10 and Node v11 and see how the output is different.
Node v10 and Node v11 clearly give two different outputs as follows:
This behavior is quite the same if you replace Promise.resolve().then
with process.nextTick
because microtasks are run after the process.nextTick callbacks are run. Let’s try to run the following snippet:
The output of Node v10 and Node v11 for the above script is as follows:
What happens here?
With the new changes in Node v11, nextTick callbacks and microtasks will run between each individual setTimeout and setImmediate callbacks, even if the timers queue or the immediates queue is not empty. In terms of setTimeout and Promise callbacks, the new changes in Node v11 match the browser behavior which improves the reusability of browser JavaScript in Node.js. However, this significant change could potentially break existing Node.js applications which explicitly rely on the old behavior. Therefore, if you are upgrading to Node v11 or above (preferably the next LTS v12), you might need to consider this as important.
References:
- MacroTask and MicroTask execution order https://github.com/nodejs/node/issues/22257
- PR: timers: run nextTicks after each immediate and timer https://github.com/nodejs/node/pull/22842
Top comments (0)