NodeJs event loop keep switching between nextTick queue and promise microtask queue - node.js

Please see below code:
Promise.resolve().then(() => console.log("promise1 resolved"));
Promise.resolve().then(() => console.log("promise2 resolved"));
Promise.resolve().then(() => {
console.log("promise3 resolved");
process.nextTick(() => {
console.log("Inner Next tick");
});
});
Promise.resolve().then(() => console.log("promise4 resolved"));
setImmediate(() => console.log("set immediate1"));
setImmediate(() => console.log("set immediate2"));
process.nextTick(() => console.log("next tick1"));
process.nextTick(() => console.log("next tick2"));
setTimeout(() => console.log("set timeout"), 0);
Output of this code is:
next tick1
next tick2
promise1 resolved
promise2 resolved
promise3 resolved
promise4 resolved
Inner Next tick
set timeout
set immediate1
set immediate2
What i cannot understand is why is the callback for innerTextTick i.e console.log("Inner Next tick") executed before setTimeout.
As per my understanding, first the nextTick queue will be executed, nextTick 1 and 2, then all the resolved Promises and then to the timers phase, which happens as expected. But in the thid promise, i have registered a new callBack in the nextTick queue which should be called after the event loop reaches the nextTick queue again, i.e after the timers phase.
But why does the event loop go back from the promise microtask queue instead of proceeding to the next phase, i.e the timers queue.

https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/#process-nexttick:
You may have noticed that process.nextTick() was not displayed in
the diagram, even though it's a part of the asynchronous API. This is
because process.nextTick() is not technically part of the event
loop. Instead, the nextTickQueue will be processed after the current
operation is completed, regardless of the current phase of the event
loop. Here, an operation is defined as a transition from the
underlying C/C++ handler, and handling the JavaScript that needs to be
executed.
Looking back at our diagram, any time you call process.nextTick() in
a given phase, all callbacks passed to process.nextTick() will be
resolved before the event loop continues.

Because in the global scope of main executed file, the Event Loop doesn't come into play yet. All the Promise.resolve() in global goes into MicrotaskQueue and all global nextTick goes into nextTick queue. So your inner nextTick goes into the nextTick queue before the Timeouts Phase of the Event Loop run for the first time. That's also why promise4 resolved even before Inner Next Tick too.

Related

can we make synchronous task asynchronous so that we don't invest our whole node process doing the one heavy task and instead continue other tasks?

I have the following code
async function func2() {
const promise = new Promise((res,rej) => {
const arr = new Array(10000000).fill('hehehehe');
const arr2 = JSON.stringify(arr);
const arr3 = new Array(10000000).fill('hehehehe');
return res('Completed Execution Of function #2');
})
return promise
}
async function func1() {
const promise = new Promise((res,rej) => {
const arr = [1,2,3];
const stringified = JSON.stringify(arr);
return res('Completed Execution of function #1');
})
return promise
}
func2().then(res => console.log(res))
func().then(res => console.log(res))
console.log('I am After Part')
The output of Above script is
As per my understanding, both function calls are passed to some services behind the scenes but func1 should get resolved first as there are no heavy calculations, but we see in output func2's response gets printed first and later func1's, What is the reason behind it??
Also, can we purposefully build this behavior in any other way??
There are a few background concepts that need to be covered to understand the behaviour you are seeing.
1. Tasks and the event loop:
https://developer.mozilla.org/en-US/docs/Web/API/HTML_DOM_API/Microtask_guide#tasks
Javascript has a task queue for managing callbacks and asynchronous behaviour.
Each task is run synchronously to completion before the next task is started (see next section).
JavaScript is single-threaded so only one task will run at a time.
(Note: for simplicity we're ignoring Workers; having a good understanding of tasks and how the JavaScript runtime works is necessary before looking at how Workers work within JavaScripts execution model)
The task queue is processed in order (i.e. the oldest task is processed next) and the task must finish before the next task will start.
Microtasks are special type of task that will run at the end of the current task before the next task runs (all current microtasks are completed before the next task will start).
Creating a promise (as shown in your example) queues a new microtask.
For simplicity I won't make any further distinction between tasks and microtasks and will refer to them both as tasks.
2. JavaScript's "run-to-completion" semantics:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop#run-to-completion
https://www.pineboat.in/post/javascript-run-to-completion-event-loop-asynchronous-foundations/
https://exploringjs.com/es6/ch_async.html#_run-to-completion-semantics
Once a task has started, all of the JavaScript code within that task will run synchronously to completion before the next task or microtask is run.
The code can create new promises or add callbacks, but those callbacks will be invoked after the current task has completed.
3. Here is nodejs's documentation of the event loop.
It can give more insight into the details of how the event loop and tasks work.
https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/#what-is-the-event-loop
4. MDN article introducing asynchronous JavaScript
https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Introducing
With that background we can look at your question to explain what's happening.
both function calls are passed to some services behind the scenes
Both functions are queued as tasks to be executed later.
They are queued in the order that you create the promises:
func2().then(res => console.log(res))
func().then(res => console.log(res))
func2 is queued first and func is queued second.
but func1 should get resolved first as there are no heavy calculations
but we see in output func2's response gets printed first and later func1's, What is the reason behind it??
JavaScript doesn't have any concept of how "heavy" a function is.
All it knows is that func2 was queued first and func was queued second, so that is the order in which the tasks will run.
Once the func2 task is started it must finish before the next func task can be started.
That explains the output you are seeing.
The way to change the order of output would be to change how the tasks are queued and run.
A trivial answer is to change the order in which the tasks are queued:
func().then(res => console.log(res))
func2().then(res => console.log(res))
But that's not a very interesting answer.
Instead, lets use our understanding of tasks to change the result.
We observe that func2 does a lot of work that must be completed before the next task can start.
So, what if we break up that work into smaller tasks?
If you can do part of the work in one task, then do the rest of the work in another task, that gives func a chance to run in between.
Here is an example that splits up the work and uses setTimeout() to queue a new task that completes the work of func2.
async function func2() {
const promise = new Promise((res,rej) => {
const arr = new Array(10000000).fill('hehehehe');
const arr2 = JSON.stringify(arr);
// the callback passed to setTimeout will be queued
// as a new task and executed later.
setTimeout(() => {
const arr3 = new Array(10000000).fill('hehehehe');
return res('Completed Execution Of function #2');
}, 0);
})
return promise
}
You could make any sort of an async call to queue a new task (e.g. fetch/https request, reading/writing to the filesystem, or making a database call).

Multiple Client Requests in NodeJs

If i hit /route2 , i get reponse 15s later,but during that time /route1 gives response immediately.
Shouldn't server wait for 15s and then give the response to /route1.
I read https://medium.com/#cinish/nodejs-multiple-client-requests-694d6353218b but could not the idea.
What i understood is as /route2 is hit console P1 then as there is setTimeout() ,put it in external thread,then console P2 then as setTimeout() ,put it in external thread.Now wait for setTimeouts() to finish.
(At this time the event loop must be busy as it waiting for p1 and p2 fulfillment, so it should accept no new client request).
But it does. Why?
app.get("/route1", (req, res) => {
res.send("Route1");
});
app.get("/route2", async (req, res) => {
const p1 = new Promise((resolve, reject) => {
console.log("P1");
setTimeout(() => {
console.log(5);
resolve();
}, 15000);
})
const p2 = new Promise((resolve, reject) => {
console.log("P2")
setTimeout(() => {
console.log(1);
resolve();
}, 1000);
});
const ans = await Promise.all([p1, p2]);
res.send("Route2");
})
setTimeout() is "non-blocking". That means it sets the timer, returns immediately from the setTimeout() and then continues to execute the rest of the code.
So, in your case /route2 creates the two promises, sets two timers and then waits for Promise.all() to finish. At the point it hits the await, your async route handler returns a promise and returns control back to the event loop.
So, as soon as the /route1 request arrives, it's ready to be processed. /route2 is not active until both timers are done.
Then, when the longer timer is ready to fire, the next time the JS interpreter goes back to the event loop to check for anything else to do, it will see that timer, process the timer callback and that timer callback will resolve the p1 promise and then cause the Promise.all() to be resolved.
If i hit /route2 , i get reponse 15s later,but during that time /route1 gives response immediately. Shouldn't server wait for 15s and then give reponse to /route1.
As explained about setTimeout() is non-blocking so while the /route2 handler is waiting for the two timers to fire, nodejs is free to process other events (like the incoming /route1 event).
What i understood is as /route2 is hit console P1 then as there is setTimeout() ,put it in external thread,then console P2 then as setTimeout() ,put it in external thread.Now wait for setTimeouts() to finish.
Timers are not run in external threads. They are a somewhat unique design in the nodejs event loop. Timers are stored in a sorted linked list in the order of when they are due. Each time the JS interpreter gets back to the event loop and gets to the timer section of the event loop, it just checks the front most timer in the linked list to see if its time has arrived. If so, it triggers that event. If not, it just goes and looks for other types of events.
Eventually, if the event loop has nothing to do and goes to sleep, it will sleep for an appropriate time to wake up in time for the next timer scheduled to run (if nothing else wakes it up before that).
(At this time the event loop must be busy as it waiting for p1 and p2 fullfillment,so it should accept no new client request). But it does.Why?
This is your main wrong assumption. At this time, the event loop is entirely free to process other events. The two timers are set and the event loop will run their callbacks when their time is due, but until then the event loop will process any other incoming events like the /route1 request.

Why crypto.pbkdf2Sync get executed before setTimeout() in Node JS?

I'm struggled to understand how crypto's result returned before nextTick, Immediate or timeout.
I know this is synchronous crypto so it block the event loop, but how exactly it block when others function got called first and time to finish is faster than cryto?
The order of code line here is not important?
const fs = require('fs');
const crypto = require('crypto');
const start = Date.now();
setTimeout(() => console.log('Timer 1 finished'), 0);
setImmediate(() => console.log('Immediate 1 finished'));
fs.readFile('test-file.txt', () => {
console.log('Reading file.... and finished');
console.log('-----------------');
setTimeout(() => console.log('Timer 2 finished'), 0);
setTimeout(() => console.log('Timer 3 finished'), 2000);
setImmediate(() => console.log('Immediate 2 finished'));
process.nextTick(() => console.log('Process nextTick'));
crypto.pbkdf2Sync('password', 'salt', 100000, 1024, 'sha512');
console.log(Date.now() - start, 'Password encrypted');
crypto.pbkdf2Sync('password', 'salt', 100000, 1024, 'sha512');
console.log(Date.now() - start, 'Password encrypted');
crypto.pbkdf2Sync('password', 'salt', 100000, 1024, 'sha512');
console.log(Date.now() - start, 'Password encrypted');
crypto.pbkdf2Sync('password', 'salt', 100000, 1024, 'sha512');
console.log(Date.now() - start, 'Password encrypted');
});
Result
Timer 1 finished
Immediate 1 finished
Reading file.... and finished
-----------------
2003 'Password encrypted'
3980 'Password encrypted'
5961 'Password encrypted'
7961 'Password encrypted'
Process nextTick
Immediate 2 finished
Timer 2 finished
Timer 3 finished
Why crypto.pbkdf2Sync get executed before setTimeout() in Node JS?
In a nushell, because crypto.pbkdf2Sync() is synchronous and node.js must complete all synchronous code in the current event processing before it can process the completion of any other asycnhronous operations.
setTimeout(), setImmediate() and process.nextTick() all schedule their callback for a FUTURE tick of the event loop. When it's their turn to run, they insert a callback into the event queue. But, NOTHING in the event queue gets to run until the current piece of Javascript is done executing. So, that means that ALL the synchronous code in your fs.readFile() callback runs to completion before anything else can be pulled from the event queue and run.
This sequencing is all because of the "event-driven" nature of node.js. Asynchronous operations always complete on a future tick of the event loop, thus any synchronous code that is already in the process of running must complete before any asynchronous operation can get its completion callback called, even if the asynchronous operation is of the "very-soon" types such as setTimeout(..., 0), process.nextTick() or .setImmediate().
So, the sequence of events goes like this (ignoring the things before fs.readFile() as they aren't relevant since they both occur before the fs.ReadFile() callback completes.
Call fs.readFile(). It starts the file operation and immediately returns.
Node.js goes back to event loop and waits for the next event.
fs.feadFile() completes and inserts a completion callback event in the event queue.
When the interpreter has finished anything else it was running, it grabs the next event from the event queue and calls the fs.feadFile() callback.
setTimeout(..., 0) is called. That schedules a timer event for the next tick of the event loop.
setTimeout(..., 2000) is called. That schedules a timer event for 2 seconds from now.
setImmediate(...) is called. That schedules an event for a future tick of the event loop.
crypto.pbkdf2Sync(...) is called. This is a synchronous operation so it runs immediately and then finishes.
crypto.pbkdf2Sync(...) is called. This is a synchronous operation so it runs immediately and then finishes.
crypto.pbkdf2Sync(...) is called. This is a synchronous operation so it runs immediately and then finishes.
crypto.pbkdf2Sync(...) is called. This is a synchronous operation so it runs immediately and then finishes.
The callback for fs.readFile() finishes. The JS interpreter then pulls the next event from the event queue.
Based on the circumstances and the relevant priority of various types of events, the next item pulled from the event queue is the process.nextTick() callback.
Then, the setImmediate() event.
Then, the setTimeout(..., 0).
Then, after some delay, the setTimeout(..., 2000).

setImmediate() function not being called after process.nextTick() function

For this snippet:
const foo = [1, 2];
const bar = ['a', 'b'];
foo.forEach( num => {
console.log(`setting setImmmediate ${num}`);
setImmediate(() => {
console.log(`running setImmediate ${num}`);
bar.forEach(char => {
console.log(`setting nextTick ${char}`);
process.nextTick(() => {
console.log(`running nextTick ${char}`);
})
})
});
} )
The output is
$ node scratch.js
setting setImmmediate 1
setting setImmmediate 2
running setImmediate 1
setting nextTick a
setting nextTick b
running setImmediate 2
setting nextTick a
setting nextTick b
running nextTick a
running nextTick b
running nextTick a
running nextTick b
From the docs
the nextTickQueue will be processed after the current operation completes, regardless of the current phase of the event loop.
As I understand, process.nextTick() will add to the current event's nextTickQueue, and executed immediately after the current event, no matter what phase the event loop is in.
Shouldn't the output therefor be the following?
setting setImmmediate 1
setting setImmmediate 2
running setImmediate 1
setting nextTick a
setting nextTick b
running nextTick a
running nextTick b
running setImmediate 2
setting nextTick a
setting nextTick b
running nextTick a
running nextTick b
the nextTickQueue will be processed after the current operation completes, regardless of the current phase of the event loop.
I misunderstood the event loop documentation in thinking that "current operation" means the currently processing event, where in actually it means the currently processing phase.
From Danial Khan's What you should know to really understand the Node.js Event Loop/:

is process.nextTick() execute immediately

I have alrady watched this wonderfully video related to the event loop work
https://www.youtube.com/watch?v=8aGhZQkoFbQ
From some reading,I suppose process.nextTick will put the callback at the very beginning of the event queue .Also I see some people use nextTick to execute a function immediately
I try to write some code to confirm it
function printNextTick(s){
process.nextTick(function(){
console.log(s)
})
}
for (var i=0;i<5;i++){
printNextTick(i)
}
what I expect is 4,3,2,1,0,because
nextTick will always put the callback at the beginning of the queue,and the script is "execute to complete", which means no callback will be execute during "put callback to the queue","4" is push last,so after the script complete,"4" should at the first place of the queue,that is why I expect "4" comes out first
But it is not correct
================================ Update ====================================
Here the new code
function printNextTick(s){
process.nextTick(function(){
console.log("nextTick : "+s)
})
}
function printTimeout(s){
setTimeout(function(){
console.log("timeout : "+s)
},0)
}
for (var i=0;i<5;i++){
printTimeout(i)
}
for (var i=0;i<5;i++){
printNextTick(i)
}
//nextTick : 0
//nextTick : 1
//nextTick : 2
//nextTick : 3
//nextTick : 4
//timeout : 0
//timeout : 1
//timeout : 2
//timeout : 3
//timeout : 4
Just wonder why some people treat nextTick as immediate Originally,I suppose nextTick will put callback at the beginning of the event queue rather than ending as normal,for now,I suppose the queue for nextTick has a higher priority than other queues ,at least higher than queue for timeout
================== Update ===============
Not true above !
The result for code below is quite unpredictable
function printNextTick(s){
process.nextTick(function(){
console.log("nextTick : "+s)
})
}
function printTimeout(s){
setTimeout(function(){
console.log("timeout : "+s)
for (var i=0;i<5;i++){
printNextTick(i)
}
},0)
}
for (var i=0;i<5;i++){
printTimeout(i)
}
Any callbacks you pass to process.nextTick() are appended to an internal queue that gets flushed (to a maximum IIRC so as not to completely starve the rest of the event loop), in order, at the "next tick." So in your example you are first appending a callback that prints '0', then a callback that prints '1', etc. These are then executed in order and you see the numbers printed in numerical order.
Just wonder why some people treat nextTick as immediate
From the documentation:
process.nextTick() fires immediately on the same phase
setImmediate() fires on the following iteration or 'tick' of the event loop
In essence, the names should be swapped. process.nextTick() fires more immediately than setImmediate() but this is an artifact of the past which is unlikely to change. Making this switch would break a large percentage of the packages on npm. Every day more new modules are being added, which mean every day we wait, more potential breakages occur.
Originally,I suppose nextTick will put callback at the beginning of the event queue rather than ending as normal,for now,I suppose the queue for nextTick has a higher priority than other queues, at least higher than queue for timeout
From process docs:
process.nextTick() runs before any additional I/O events (including timers)
To understand the difference between nextTick and setTimeout, consider my implementation of setTimeout:
function setTimeout2(callback, ms) {
var t = process.hrtime(),
_next = () =>
process.nextTick(process.hrtime(t)[0] > !!ms?callback:_next);
_next();
}
function printNextTick(s){
process.nextTick(() => console.log('n'+s))
}
function printTimeout(s){
setTimeout2(() => {
console.log('t'+s);
for (var i=0;i<5;i++)
printNextTick(s+':'+i);
});
}
for (var i=0;i<5;i++)
printTimeout(i);
It behaves very similar to setTimeout, except it is less efficient. To answer your question, no, process.nextTick(callback) appends callback to the nextTick queue. setTimeout(callback, ms) also appends its callback to the nextTick queue if diff > ms (not >= ms).

Resources