Related
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.
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).
The following example is given in a Node.js book:
var open = false;
setTimeout(function() {
open = true
}, 1000)
while (!open) {
console.log('wait');
}
console.log('open sesame');
Explaining why the while loop blocks execution, the author says:
Node will never execute the timeout callback because the event loop is
stuck on this while loop started on line 7, never giving it a chance
to process the timeout event!
However, the author doesn't explain why this happens in the context of the event loop or what is really going on under the hood.
Can someone elaborate on this? Why does node get stuck? And how would one change the above code, whilst retaining the while control structure so that the event loop is not blocked and the code will behave as one might reasonably expect; wait
will be logged for only 1 second before the setTimeout fires and the process then exits after logging 'open sesame'.
Generic explanations such as the answers to this question about IO and event loops and callbacks do not really help me rationalise this. I'm hoping an answer which directly references the above code will help.
It's fairly simple really. Internally, node.js consists of this type of loop:
Get something from the event queue
Run whatever task is indicated and run it until it returns
When the above task is done, get the next item from the event queue
Run whatever task is indicated and run it until it returns
Rinse, lather, repeat - over and over
If at some point, there is nothing in the event queue, then go to sleep until something is placed in the event queue or until it's time for a timer to fire.
So, if a piece of Javascript is sitting in a while() loop, then that task is not finishing and per the above sequence, nothing new will be picked out of the event queue until that prior task is completely done. So, a very long or forever running while() loop just gums up the works. Because Javascript only runs one task at a time (single threaded for JS execution), if that one task is spinning in a while loop, then nothing else can ever execute.
Here's a simple example that might help explain it:
var done = false;
// set a timer for 1 second from now to set done to true
setTimeout(function() {
done = true;
}, 1000);
// spin wait for the done value to change
while (!done) { /* do nothing */}
console.log("finally, the done value changed!");
Some might logically think that the while loop will spin until the timer fires and then the timer will change the value of done to true and then the while loop will finish and the console.log() at the end will execute. That is NOT what will happen. This will actually be an infinite loop and the console.log() statement will never be executed.
The issue is that once you go into the spin wait in the while() loop, NO other Javascript can execute. So, the timer that wants to change the value of the done variable cannot execute. Thus, the while loop condition can never change and thus it is an infinite loop.
Here's what happens internally inside the JS engine:
done variable initialized to false
setTimeout() schedules a timer event for 1 second from now
The while loop starts spinning
1 second into the while loop spinning, the timer is ready to fire, but it won't be able to actually do anything until the interpreter gets back to the event loop
The while loop keeps spinning because the done variable never changes. Because it continues to spin, the JS engine never finishes this thread of execution and never gets to pull the next item from the event queue or run the pending timer.
node.js is an event driven environment. To solve this problem in a real world application, the done flag would get changed on some future event. So, rather than a spinning while loop, you would register an event handler for some relevant event in the future and do your work there. In the absolute worst case, you could set a recurring timer and "poll" to check the flag ever so often, but in nearly every single case, you can register an event handler for the actual event that will cause the done flag to change and do your work in that. Properly designed code that knows other code wants to know when something has changed may even offer its own event listener and its own notification events that one can register an interest in or even just a simple callback.
This is a great question but I found a fix!
var sleep = require('system-sleep')
var done = false
setTimeout(function() {
done = true
}, 1000)
while (!done) {
sleep(100)
console.log('sleeping')
}
console.log('finally, the done value changed!')
I think it works because system-sleep is not a spin wait.
There is another solution. You can get access to event loop almost every cycle.
let done = false;
setTimeout(() => {
done = true
}, 5);
const eventLoopQueue = () => {
return new Promise(resolve =>
setImmediate(() => {
console.log('event loop');
resolve();
})
);
}
const run = async () => {
while (!done) {
console.log('loop');
await eventLoopQueue();
}
}
run().then(() => console.log('Done'));
Node is a single serial task. There is no parallelism, and its concurrency is IO bound. Think of it like this: Everything is running on a single thread, when you make an IO call that is blocking/synchronous your process halts until the data is returned; however say we have a single thread that instead of waiting on IO(reading disk, grabbing a url, etc) your task continues on to the next task, and after that task is complete it checks that IO. This is basically what node does, its an "event-loop" its polling IO for completion(or progress) on a loop. So when a task does not complete(your loop) the event loop does not progress. To put it simply.
because timer needs to comeback and is waiting loop to finish to add to the queue, so although the timeout is in a separate thread, and may indeed finsihed the timer, but the "task" to set done = true is waiting on that infinite loop to finish
var open = false;
const EventEmitter = require("events");
const eventEmitter = new EventEmitter();
setTimeout(function () {
open = true;
eventEmitter.emit("open_var_changed");
}, 1000);
let wait_interval = setInterval(() => {
console.log("waiting");
}, 100);
eventEmitter.on("open_var_changed", () => {
clearInterval(wait_interval);
console.log("open var changed to ", open);
});
this exemple works and you can do setInterval and check if the open value changed inside it and it will work
I'm trying the following
process.on('exit', function() {
child_process.exec('echo hello', /*...*/);
}
and want to delay exit until the child process has finished.
Is this possible?
Nope, according to doc, exit event is too late to bind any async events. You should instead listen for beforeExit event.
Emitted when the process is about to exit. There is no way to prevent
the exiting of the event loop at this point, and once all exit
listeners have finished running the process will exit. Therefore you
must only perform synchronous operations in this handler.
In beforeExit you can do async operation and exit manually:
process.on('beforeExit', function() {
setTimeout(function(){ //run async code
console.log('beforeExit')
process.exit(0); //exit manually
}, 1000);
});
When I receive an "on" event on the server side, I want to start a task in parallel so it does not block the current event loop thread. Is it possible to do so? How?
I don't want to block the server side loop and I want to be able to send back a message to the client once the task is done, something such as:
client.on('execute-parallel-task', function(msg) {
setTimeout(function() {
// do something that takes a while
client.emit('finished-that-task');
},0);
// this block should return asap, not waiting for the previous call
});
I am not sure if setTimeout will do the job.
It depends what the takes a while is. If it takes a while asynchronously (you can tell because you'll have to register a callback or complete handler), and takes a while because it's blocked on something like IO, rather than CPU bound, it'll inherently be parallel.
If however, its something synchronous or CPU bound, whilst you can use setTimeout, setImmediate etc. to send back a message immediately, once the handler for setTimeout or setImmediate executes, your single thread of execution will be stuck handling that; you're not really fixing the problem, merely deferring it.
To exhibit true parallel behaviour, you'll need to launch a child process. You can use the message passing functionality to notify your worker what work to do, and to notify the parent process once the work is complete.
var cp = require('child_process');
var child = cp.fork(__dirname + '/my-child-worker.js');
n.on('message', function(m) {
if (m === "done") {
// Whey!
}
});
n.send(/* Job id, or something */);
Then in my-child-worker.js;
process.on('message', function (m) {
switch (m) {
case 'get-x':
// blah
break;
// other jobs
}
process.send('done');
});
you do not need the setTimeout.
Your function(msg) will be called once the execute parallel task finishes.
if you are designing a task to run in an async manner, you can look at something like the async lib for node.js
Async Node JS Link