Reputation: 1121
I was learning Node.js
and came across event loop and how it handles asynchronous tasks in Node.js
. As far as I understood now and I might be wrong)), when, for example, we use asynchronous, say, fs.readFile()
that readFile
will be taken out of main thread and will be passed to OS kernel
(so that kernel handles readFile while JS engine
can keep reading the rest of the code in main thread). Then, Once kernel
is done with readFile
, event is emitted which will be taken by event loop
. So, the question is after event was accepted by event loop
, will event loop execute BY ITSELF the callback or will event loop takes that callback form queue and passes the callback into call stack in order for the call stack to execute that callback?
Upvotes: 3
Views: 969
Reputation: 707158
A callback coming from the event loop starts a from-scratch, new and empty call stack. Whatever call stack it was in at the time has long since finished and returned control back to the event loop.
The callback will start with a lexical context record which gives that callback access to the appropriate lexical environment (local variables, etc... that were in scope when the callback was defined), and that is reattached to the callback before it is executed.
And, executing the callback will be the initiation of a new call stack that supports the callback itself calling other functions and makes it so when the callback returns, control will go back to the event loop.
FYI, a call stack is just a record of where to return back to when a function returns and it is referred to as a stack because it can build up and then unwind as a()
calls b()
which then calls c()
which then returns and goes back to b()
and then returns and goes back to a()
. That's the stack part. The call stack is just the storage mechanism for all this.
Since a callback called from the event loop just returns back to the event loop when it's done, the only thing on the call stack at the moment the callback is initiated will be the return address back to the event loop. So, when the callback returns, control comes back to the event loop.
So, the question is after event was accepted by event loop, will event loop execute BY ITSELF the callback or will event loop takes that callback form queue and passes the callback into call stack in order for the call stack to execute that callback?
This sounds like some possible terminology confusion. A call stack is just a stored set of return addresses. The JS interpreter uses the call stack to remember return addresses and then to jump back to return addresses when a function returns. It's the interpreter running the show, not the call stack running the show. The call stack is just data.
So, let's say you had this code:
let greeting1 = "Hello";
console.log("AA");
setTimeout(() => {
let greeting2 = "GoodBye";
console.log("A");
fs.readfile('./myfile.txt', () => {
console.log("B", greeting1)
});
console.log("C");
}, 5000);
console.log("BB");
Here's the sequence of events with that code.
setTimeout()
runs and schedules a callback to be called for 5 seconds from now.greeting1
to it so when that callback runs, it will have access to the greeting1
variable. The event loop creates a new call stack, puts its own return address on it and calls the timer callback.greeting2
, outputs console.log("A")
and runs fs.readFile()
.fs.readFile()
initiates an asynchronous operation to read that file and then returns. As part of that, it registers a completion callback to be called.console.log("C")
executes. The timer callback then returns back to the event loop.fs.readFile()
operation (which actually consists of multiple separate asynchronous operations, but I've simplified here to just one) completes when the file is finally closed. That file closing triggers a callback to get called in the fs
module (internal to the fs.readFile()
code).fs
module. When that executes, it then calls your callback and you see the output from console.log("B", greeting)
.The console output from this would look like:
AA
BB
A
C
B, Hello
Sorry, some questions again, firstly, is it really true that there is OS kernel that handles asynchronous readFile() and once readFile is done, passes the emitted event to event loop? Secondly, so after event loop gets emitted event concerning the completion of readFile(), event loop just calls the callback. But as you can see, all hard was done by OS kernel and event loop just executes the easy part which is callback. So, is it true that even callback can be blocking thus thread pool is used if callback is blocking being hard for event loop?
Let's not talk about readFile()
for a moment, because that's a multi-stage operation of opening the file, doing one or more reads and then closing the file and that's a mix of Javascript, native C++ code in node.js and OS library calls. So, that's a bit more complicated. Let's pick something similar, but simpler. Let's pick a single operation like fs.stat()
with this code:
fs.stat('./myfile.txt', (stats) => {
console.log(stats);
});
console.log("A");
Here are the steps:
fs.stat()
.stat()
call. That call sets up some context to allow Javascript data to be passed to C++ code and then calls a C++ function (still nodejs-specific code, but now in C++ code) for the stat()
operation.stat()
operation gets returned back to from the C++ and it returns back to your Javascript where it continues executing other Javascript.console.log("A")
in the code above and outputs that to the console.fs.stat()
call, sets that up and calls the Javascript callback associated with that completion event.console.log(stats)
.Upvotes: 5