I'm learning the mechanism of Event-Loop in Node.js, and I'm doing some exercises, but have some confusions as explained bellow.
const fs = require("fs");
setTimeout(() => console.log("Timer 1"), 0);
setImmediate(() => console.log("Immediate 1"));
fs.readFile("test-file-with-1-million-lines.txt", () => {
console.log("I/O");
setTimeout(() => console.log("Timer 2"), 0);
setTimeout(() => console.log("Timer 3"), 3000);
setImmediate(() => console.log("Immediate 2"));
});
console.log("Hello");
I expected to see the following output:
Hello
Timer 1
Immediate 1
I/O
Timer 2
Immediate 2
Timer 3
but I get the following output:
Hello
Timer 1
Immediate 1
I/O
Immediate 2
Timer 2
Timer 3
Would you please clarify for me how are these lines executed step by step.
First off, I should mention that if you really want asynchronous operation A to be processed in a specific order with relation to asynchronous operation B, you should probably write your code such that it guarantees that without relying on the details of exactly what gets to run first. But, that said, I have run into issues where one type of asynchronous operation can "hog" the event loop and starve other types of events and it can be useful to understand what's really going on inside if/when that happens.
Broken down to its core, your question is really about why Immediate2 logs before Timer2 when scheduled from within an I/O callback, but not when called from top level code? Thus it is inconsistent.
This has to do with where the event loop is in its cycle through various checks it is doing when the setTimeout() and setImmediate() are called (when they are scheduled). It is somewhat explained here: https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/#setimmediate-vs-settimeout.
If you look at this somewhat simplified diagram of the event loop (from the above article):
You can see that there are a number of different parts to the event loop cycle. setTimeout() is served by the "timers" block at the top of the diagram. setImmediate() is served in the "check" block near the bottom of the diagram. File I/O is served in the "poll" block in the middle.
So, if you schedule both a setImmediate(fn1) and a setTimeout(fn2, 0) from within a file I/O callback (which is your case for Intermediate2 and Timer2), then the event loop processing happens to be in the poll phase when these two are scheduled. So, the next phase of the event loop is the "check" phase and the setImmediate(fn1) gets processed. Then, after the "check" phase and the "close callbacks" phase, then it cycles back around to the "timers" phase and you get the setTimeout(fn2,0).
If, on the other hand, you call those same two setImmediate() and setTimeout() from code that runs from a different phase of the event loop, then the timer might get processed first before the setImmediate() - it will depend upon exactly where that code was executed from in the event loop cycle.
This structure of the event loop is why some people describe setImmediate() as "it runs right after I/O" because it's positioned in the loop to be processed right after the "poll" phase. If you are in the middle of processing some file I/O in an I/O callback and you want something to run as soon as the stack unwinds, you can use setImmediate() to accomplish that. It will always run after the current I/O callback finishes, but before timers.
Note: Missing from this simplified description is promises which have their own special treatment. Promises are considered microtasks and they have their own queue. They get to run a lot more often. Starting with node v11, they get to run in every phase of the event loop. So, if you have three pending timers that are ready to run and you get to the timer phase of the event loop and call the callback for the first pending timer and in that timer callback, you resolve a promise, then as soon as that timer callback returns back to the system, then it will serve that resolved promise. So, microtasks (such as promises and process.nextTick()) get served (if waiting to run) between every operation in the event loop, not just between phases of the event loop, but even between pending events in the same phase. You can read more about these specifics and the changes in node v11 here: New Changes to the Timers and Microtasks in Node v11.0.0 and above.
I believe this was done to improve the performance of promise-related code as promises became more of a central part of the nodejs architecture for asynchronous operations and there is also some standards-related work in this area too to make this consistent across different JS envrionments.
Here's another reference that covers part of this:
Nodejs Event Loop - interaction with top-level code
The reason for this output is the asynchronous nature of javascript.
You set the first 2 outputs in a sort of timeout with the execution time to be 0 this makes them still wait a tick.
Next you have the file read which takes a while to be finished and thus delays the execution of the functions in the callback
The first console.log within the callback is fired as soon as the callback is executed and the rest within the callback follows the first part of your code
Lastly you have the console.log at the bottom which gets executed at first because there is no delay for it and it does not need to wait till the next tick.
As some added help, check out this video.
https://youtu.be/cCOL7MC4Pl0
The presenter gives an amazing talk on the event loop. I think it is a great resource.
While this is particularly for the browser, many aspects are shared in Node.
Related
I've seen many people saying that Promise.all can't achieve parallelism, since node/javascript runs on a single-threaded environment. However, if i, for instance, wrap 5 promises inside a Promise.all, in which every single one of the promises resolves after 3 seconds (a simple setTimeout promise), how come Promise.all resolves all of them in 3 seconds instead of something like 15 seconds (5 x 3 sec each)?
See the example below:
function await3seconds () {
return new Promise(function(res) {
setTimeout(() => res(), 3000)
})
}
console.time("Promise.all finished in::")
Promise.all([await3seconds(), await3seconds(), await3seconds(), await3seconds(), await3seconds()])
.then(() => {
console.timeEnd("Promise.all finished in::")
})
It logs:
Promise.all finished in::: 3.016s
How is this behavior possible without parallelism? Concurrent execution wouldn't be able to proccess all of these promises in 3 seconds either.
It's particularly useful to understand what this line of code actually does:
Promise.all([await3seconds(), await3seconds(), await3seconds(), await3seconds(), await3seconds()]).then(...)
That is fundamentally the same as:
const p1 = await3seconds();
const p2 = await3seconds();
const p3 = await3seconds();
const p4 = await3seconds();
const p5 = await3seconds();
Promise.all([p1, p2, p3, p4, p5]).then(...)
What I'm trying to show here is that ALL your functions are executed serially one after the other in the order declared and they have all returned BEFORE Promise.all() is even executed.
So, some conclusions from that:
Promise.all() didn't "run" anything. It accepts an array of promises and it justs monitors all those promises, collects their results in order and notifies you (via .then() or await) when they are all done or tells you when the first one rejects.
Your functions are already executed and have returned a promise BEFORE Promise.all() even runs. So, Promise.all() doesn't determine anything about how those functions run.
If the functions you were calling were blocking, the first would run to completion before the second was even called. Again, this has nothing to do with Promise.all() before the functions are all executed before Promise.all() is even called.
In your particular example, your functions each start a timer and immediately return. So, you essentially start 5 timers within ms of each other that are all set to fire in 3 seconds. setTimeout() is non-blocking. It tells the system to create a timer and gives the system a callback to call when that timer fires and then IMMEDIATELY returns. Sometime later, when the event loop is free, the timer will fire and the callback will get called. So, that's why all the timers are set at once and all fire at about the same time. If you wanted them to each be spread out by 3 seconds apart, you'd have to write this code differently, either to set increasing times for each timer or to not start the 2nd timer until the first one fires and so on.
So, what Promise.all() allows you to do is to monitor multiple asynchronous operations that are, by themselves (independent of Promise.all()) capable of running asynchronously. Nodejs itself, nothing to do with Promise.all(), has the ability to run multiple asynchronous operations in parallel. For example, you can make multiple http requests or make multiple read requests from the file system and nodejs will run those in parallel. They will all be "in flight" at the same time.
So, Promise.all() isn't enabling parallelism of asynchronous operations. That capability is built into the asynchronous operations themselves and how they interact with nodejs and how they are implemented. Promise.all() allows you to track multiple asynchronous operations and know when they are all done, get their results in order and/or know when there was an error in one of the operations.
If you're curious how timers work in nodejs, they are managed by libuv which is a cross platform library that nodejs uses for managing the event loop, timers, file system access, networking and a whole bunch of things.
Inside of libuv, it manages a sorted list of pending timers. The timers are sorted by their next time to fire so the soonest timer to fire is at the start of the list.
The event loop within nodejs goes in a cycle to check for a bunch of different things and one of those things is to see if the current head of the timer list has reached its firing time. If so, it removes that timer from the list, grabs the callback associated with that timer and calls it.
Other types of asynchronous operations such as file system access work completely differently. The asynchronous file operations in the fs module, actually use a native code thread pool. So, when you request an asynchronous file operation, it actually grabs a thread from the thread pool, gives it the job for the particular file operation you requested and sends the thread on its way. That native code thread, then runs independently from the Javascript interpreter which is free to go do other things. At some future time when the thread finishes the file operation, it calls a thread safe interface of the event loop to add a file completion event to a queue. When whatever Javascript is currently executing finishes and returns control back to the event loop, one of the things the event loop will do is check if there are any file system completion events waiting to be executed. If so, it will remove it from the queue and call the callback associated with it.
So, while individual asynchronous operation can themselves run in parallel if designed appropriately (usually backed by native code), signaling completion or errors to your Javascript all runs through the event loop and the interpreter is only running one piece of Javascript at a time.
Note: this is ignoring for the purposes of this discussion, the WorkerThread capability which actually fires up a whole different interpreter and can run multiple sets of Javascript at the same time. But, each individual interpreter still runs your Javascript single threaded and is still coordinated with the outside world through the event loop.
First, Promise.all has nothing to do with JS code running in parallel. You can think of Promise.all as code organizer, it puts code together and wait for response.
What's responsible for the code to run in a way that "looks like" it's parallel is the event-based nature of JS. So in your case:
Promise.all([await3seconds(), await3seconds(),
await3seconds(), await3seconds(), await3seconds()])
.then(() => {
console.timeEnd("Promise.all finished in::")
})
Let's say that each function inside Promise.all is called a1 : a5, What will happen is:
The Event loop will take a1 : a5 and put them in the "Callback Queue/Task Queue" sequentially (one after the other), But it will not take too much time, because it's just putting it in there, not executing anything.
The timer will start immediately after each function is put by the Event Loop in the "Callback Queue/Task Queue" (so there will be a minor delay between the start of each one).
Whenever a timer finishes, the Event loop will take the related callback function and put it in the "Call Stack" to be executed.
Promise.all will resolve after the last function is popped out of the "Call Stack".
As you can see in here
Promise.all finished in::: 3.016s
The 0.16s delay is a combination between the time the Event loop took to put those callback functions sequentially in the "Callback Queue/Task Queue" + the time each function took to execute the code inside it after their timer has finished.
So the code is not executed in parallel, it's still sequential, but the event-based nature of JS is used to mimic the behavior of parallelism.
Look at this article to relate more to what I am trying to say.
Synchronous vs Asynchronous JavaScript
No they are not executed in parallel but you can conceptualize them this way. This is just how the event queue works. If each promise contained a heavy compute task, they would still be executed one at a time -
function await3seconds(id) {
return new Promise(res => {
console.log(id, Date.now())
setTimeout(_=> {
console.log(id, Date.now())
res()
}, 3000)
})
}
console.time("Promise.all finished in::")
Promise.all([await3seconds(1), await3seconds(2), await3seconds(3), await3seconds(4), await3seconds(5)])
.then(() => {
console.timeEnd("Promise.all finished in::")
})
time
1
2
3
4
5
1621997095724
start
⌛
⌛
⌛
⌛
1621997095725
↓
start
⌛
⌛
⌛
1621997095725
↓
↓
start
⌛
⌛
1621997095725
↓
↓
↓
start
⌛
1621997095726
↓
↓
↓
↓
start
1621997098738
end
↓
↓
↓
↓
1621997098740
✓
end
↓
↓
↓
1621997098740
✓
✓
end
↓
↓
1621997098741
✓
✓
✓
end
↓
1621997098742
✓
✓
✓
✓
end
In this related Q&A we build a batch processing Pool that emulates threads. Check it out if that kind of thing interests you!
I am pretty new to JS event loop, I wonder if anyone could give me a brief walk thru about how js engine run this:
function start(){
setTimeout(function(){
console.log("Timeout")
}, 0)
setImmediate(function(){
console.log("Immediate")
})
process.nextTick(function(){
console.log("next tick")
})
}
The result is :
next tick
Timeout
Immediate
I thought when JS engine runs this,
it parses the script, set Timer, set Immediate, set nextTick queue, then goes into POLL stage, checks if anything queued there(nothing in this case)
Before moving to CHECK stage, it runs nextTick queue, print "next tick".
Moves to CHECK stage, run immediate queue, print "Immediate"
Loops back to TIMER stage, print "Timeout"
My confuse is why setTimeout print out before Immediate?
PS,
After I set Timeout delay from 0 to 3 or more, then the order is:
next tick
Immediate
Timeout
But this still does not explain why previous order JS event loop runs.
I wonder is there anything I missed?
setImmediate queues the function behind whatever I/O, event, callbacks that are already in the event queue. So, in this case setTimeout is already in queue.
I think you should read https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/ again. It has answer to all your questions. I am quoting following lines from above mentioned document
setImmediate() vs setTimeout()
setImmediate and setTimeout() are similar, but behave in different ways depending on when they are called.
setImmediate() is designed to execute a script once the current poll phase completes.
setTimeout() schedules a script to be run after a minimum threshold in ms has elapsed.
The order in which the timers are executed will vary depending on the context in which they are called.
Understanding process.nextTick()
process.nextTick() is not technically part of the event loop. Instead, the nextTickQueue will be processed after the current operation completes, regardless of the current phase of the event loop.
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. This can create some bad situations because it allows you to "starve" your I/O by making recursive process.nextTick() calls, which prevents the event loop from reaching the poll phase.
I have been going throug node JS training on newboston channel (link https://www.youtube.com/watch?v=KsjrN-T3ZCs). and Example was given on how Node provides better performance is shown below
function placeAnOrder(orderNumber) {
console.log("Order placed", orderNumber);
cookAndDeliver(function () {
console.log("Delivered order", orderNumber);
});
};
function cookAndDeliver(callback){
setTimeout(callback, 5000);
};
placeAnOrder(1);
placeAnOrder(11);
placeAnOrder(111);
placeAnOrder(1111);
placeAnOrder(11111);
Should you run the sample, you will notice that first all order placed at once, which is normal, and then all of the orders are also delivered once, which is not normal since there is already a timeout constraint of 5 seconds applied per call.
The explanation given by the video was that the call back is none-obstructive and the code will execute unintrrupted, which I concur with, what I don't get is why the node did not honor the timeout value?
My guess is that since it operates in an asynchronous mode then it means that a separate thread is going to handle each call, is my assumption correct?
Here's the sequence of events:
Call first placeAnOrder()
It calls cookAndDeliver()
It calls setTimeout()
The setTimeout() schedules a timer event for 5 seconds from now and returns immediately.
Call second placeAnOrder()
It calls cookAndDeliver()
It calls setTimeout()
The setTimeout() schedules a timer event for 5 seconds from now and returns immediately.
Call second placeAnOrder()
It calls cookAndDeliver()
It calls setTimeout()
The setTimeout() schedules a timer event for 5 seconds from now and returns immediately.
Repeat this two more times for a total of 5 timers scheduled to go about 5 seconds from now
The Javascript interpreter returns control back to the event loop as it has nothing further to do for awhile
Then about 5 seconds from now, an internal part of Javascript places the first timer event into the event queue
If the Javascript interpreter is not doing anything at that moment, then it fires that event and calls the callback associated with that timer event.
That callback executes.
When that callback executes, there's another timer event in the event queue and the JS interpreter calls its callback to process it.
After that callback is done, the next 3 timer events are pulled, one at a time, from the event queue and their callbacks are executed.
There are no Javascript threads here. Javascript in node.js is single threaded. Everything runs through a central event queue. There may be internals to Javascript that use threads in order to carry out asynchronous operations (for example, async file I/O does use threads internal to its implementation, but not for running the Javascript). When those threads are done, they place an event into the event queue and when the single node.js Javascript thread is done executing what it was doing, it then grabs the next event from the event queue and executes the callback associated with it.
My guess is that since it operates in an asynchronous mode then it means that a separate thread is going to handle each call, is my assumption correct?
No, that is not correct. setTimeout() schedules a timer event for some future time. There is no Javascript thread associated with a timer event. There may or may not be a single system thread that manages all timer events (that is implementation specific and doesn't really matter - It could also be that whenever Javascript gets back to the event loop, it just checks to see if its time to fire the next timer event). When the time arrives for the timer to fire, an event is inserted into the Javascript event queue. The JS interpreter then picks up that event from the event loop, the next time it's done processing and is looking for the next event to process.
An example was given on how Node provides better performance is shown below
I think what they were trying to show is that because of the node.js asynchronous event-driven design, there are NOT multiple threads for asynchronous events like timers. That allows it to scale better when there are lots of asynchronous operations in flight at the same time because its model is more efficient than a model where there is a system thread for every timer.
Should you run the sample, you will notice that first all order placed at once, which is normal, and then all of the orders are also delivered once, which is not normal since there is already a timeout constraint of 5 seconds applied per call.
The code essentially runs 5 setTimeout() calls in a row that all set a timer to fire 5 seconds from now. setTimeout() is non-blocking. That means it schedules the timer for 5 seconds from now and then IMMEDIATELY goes on to the next line of code which also schedules a timer for 5 seconds from now. So you end up with 5 timers all scheduled for about 5 seconds from now (perhaps varying only be a few ms of execution time from one setTimeout() call to the next. The end result is that all 5 timers will fire in about 5 seconds and they will fire in the order they were scheduled.
The explanation given by the video was that the call back is none-obstructive and the code will execute unintrrupted, which I concur with, what I don't get is why the node did not honor the timeout value?
Because setTimeout() is non-blocking (as are all asynchronous operations in node.js). It schedules the timer for some future time and then immediately returns (that's the non-blocking part). It does not wait 5 seconds to return. It returns immediately and executes whatever code comes next. Then 5 seconds from now (when the JS interpreter has nothing else to do), the timer callback will get called.
It does honor the timeout. The timeout is for the callback, and not the execution of the function. So what happened is that all of your function calls executed asynchronously, and they all waited five seconds, and they all returned. So they all took five seconds between starting and finishing. Node doesn't have to wait for the first function to complete before the second one to complete.
Basically for a non-trivial example i.e. waiting on a db call. you don't have to wait for one db call to finish before sending a second db call. And if the db is threaded, you can get the results pretty much at the same time.
I am a bit confused about when the event loop spins in the browser.
The questions are:
Does a task and the pending microtasks, happen in the same loop iteration/turn/tick?
Which are the actual conditions that need to be met in order for the loop to turn?
Are these conditions the same in node.js event loop? - I don't know if this is a stupid question.
Let's say we have a webpage and in the front end we have JavaScript code that schedules a task and waits for a promise (which is a microtask). Is the execution of the promise considered to happen in the same turn of the event loop as the task, or in different iterations?
I currently assume that they all happen in the same iteration. Since if betting otherwise, in the case of microtasks executing while mid-task, that would mean that the task would require multiple loop iterations in order to fully complete. Which seems wired to me. Would it be correct to also say that the update rendering part, that may occur after each task, happens in the same loop turn?
Thank you in advance!
------------------------------------------------------------------------------------
I know I am supposed to add a comment, but it is going to be a long one, and I also need to write code, so I am editing the question and asking for clarification here.
#T.J. Crowder Thank you so much for your time and detailed explanation!
I had indeed misread "microtasks are processed after callbacks (as long as no other JavaScript is mid-execution)" in this great article and had gotten a bit confused.
I also had questions about the 4ms setTimout for which I couldn't find information about, so thanks for that info also.
One last thing, though... If we were to mark the loop ticks between the example code, where would we put them (assuming console.logs do not exist)?
Suppose we have a function named exampleCode, having the following body:
setTimeout(setTimeoutCallback, 0);
Promise.resolve().then(promiseCallback);
For the above code, my guess would be...
Just before executing exampleCode (macro)task:
first loop tick
setTimeoutCallback (macro)task scheduling
Promise.then microtask scheduling
promiseCallback execution
second loop tick
setTimeoutCallback execution
third loop tick
Or is there an additional loop tick between between Promise.then microtask scheduling and promiseCallback execution ?
Thank you in advance once again!
Does a task and the pending microtasks, happen in the same loop iteration/turn/tick?
The task occurs, then when it ends, any pending microtasks it scheduled are run.
Which are the actual conditions that need to be met in order for the loop to turn?
It's not clear what you mean by this. It may be easier to think in terms of jobs and a job queue (which is the ECMAScript spec's terminology): If there is a pending job and the thread servicing that queue is not doing something else, it picks up the job and runs it to completion.
Are these conditions the same in node.js event loop?
Close enough, yes.
Let's say we have a webpage and in the front end we have JavaScript code that schedules a task and waits for a promise (which is a microtask). Is the execution of the promise considered to happen in the same turn of the event loop as the task, or in different iterations?
In a browser (and in Node), it happens after the task completes, when the task's microtasks (if any) are run, before the next queued task/job gets picked up.
For instance:
// This code is run in a task/job
console.log("Scheduling (macro)task/job");
setTimeout(() => {
console.log("timeout callback ran");
}, 0);
console.log("Scheduling microtask/job");
Promise.resolve().then(() => {
console.log("promise then callback ran");
});
console.log("main task complete");
On a compliant browser (and Node), that will output:
Scheduling (macro)task/job
Scheduling microtask/job
main task complete
promise then callback ran
timeout callback ran
...because the microtask ran when the main task completed, before the next macrotask ran.
(Note that setTimeout(..., 0) will indeed schedule the timer to run immediately on compliant browsers provided it's not a nested timeout; more here. You'll see people saying there is no "setTimeout 0", but that's outdated information. It's only clamped to 4ms if the timer's nesting level is > 5.)
More to explore:
MDN Concurrency Model and Event Loop
ECMAScript Spec: Jobs and Job Queues
WHAT-WG "HTML5" Spec: Event Loops, Processing Model, Timers, and Timer Initialization Steps
Re the code and question in the edit/comment:
setTimeout(setTimeoutCallback, 0);
Promise.resolve().then(promiseCallback);
Your guess looks pretty good. Here's how I'd describe it:
Schedule the task to run that script
(When thread is next free)
Pick up the next task (from #1 above)
Run that task:
Create the task for the timer callback
In parallel, queue the task when the time comes
Queue a microtask for the promise then callback
End of task
Microtask check
Run the then callback
(When thread is next free)
Pick up the next task (the one for the timer callback)
Run the task
End of task
Microtask check (none to do in this case)
The processing model text explicitly calls out that the task ends prior to the microtask check, but I don't think that's observable in any real way.
I am thinking about it and this is what I came up with:
Let's see this code below:
console.clear();
console.log("a");
setTimeout(function(){console.log("b");},1000);
console.log("c");
setTimeout(function(){console.log("d");},0);
A request comes in, and JS engine starts executing the code above step by step. The first two calls are sync calls. But when it comes to setTimeout method, it becomes an async execution. But JS immediately returns from it and continue executing, which is called Non-Blocking or Async. And it continues working on other etc.
The results of this execution is the following:
a c d b
So basically the second setTimeout got finished first and its callback function gets executed earlier than the first one and that makes sense.
We are talking about single-threaded application here. JS Engine keeps executing this and unless it finishes the first request, it won't go to second one. But the good thing is that it won't wait for blocking operations like setTimeout to resolve so it will be faster because it accepts the new incoming requests.
But my questions arise around the following items:
#1: If we are talking about a single-threaded application, then what mechanism processes setTimeouts while the JS engine accepts more requests and executes them? How does the single thread continue working on other requests? What works on setTimeout while other requests keep coming in and get executed.
#2: If these setTimeout functions get executed behind the scenes while more requests are coming in and being executed, what carries out the async executions behind the scenes? What is this thing that we talk about called the EventLoop?
#3: But shouldn't the whole method be put in the EventLoop so that the whole thing gets executed and the callback method gets called? This is what I understand when talking about callback functions:
function downloadFile(filePath, callback)
{
blah.downloadFile(filePath);
callback();
}
But in this case, how does the JS Engine know if it is an async function so that it can put the callback in the EventLoop? Perhaps something like the async keyword in C# or some sort of an attribute which indicates the method JS Engine will take on is an async method and should be treated accordingly.
#4: But an article says quite contrary to what I was guessing on how things might be working:
The Event Loop is a queue of callback functions. When an async
function executes, the callback function is pushed into the queue. The
JavaScript engine doesn't start processing the event loop until the
code after an async function has executed.
#5: And there is this image here which might be helpful but the first explanation in the image is saying exactly the same thing mentioned in question number 4:
So my question here is to get some clarifications about the items listed above?
1: If we are talking about a single-threaded application, then what processes setTimeouts while JS engine accepts more requests and executes them? Isn't that single thread will continue working on other requests? Then who is going to keep working on setTimeout while other requests keep coming and get executed.
There's only 1 thread in the node process that will actually execute your program's JavaScript. However, within node itself, there are actually several threads handling operation of the event loop mechanism, and this includes a pool of IO threads and a handful of others. The key is the number of these threads does not correspond to the number of concurrent connections being handled like they would in a thread-per-connection concurrency model.
Now about "executing setTimeouts", when you invoke setTimeout, all node does is basically update a data structure of functions to be executed at a time in the future. It basically has a bunch of queues of stuff that needs doing and every "tick" of the event loop it selects one, removes it from the queue, and runs it.
A key thing to understand is that node relies on the OS for most of the heavy lifting. So incoming network requests are actually tracked by the OS itself and when node is ready to handle one it just uses a system call to ask the OS for a network request with data ready to be processed. So much of the IO "work" node does is either "Hey OS, got a network connection with data ready to read?" or "Hey OS, any of my outstanding filesystem calls have data ready?". Based upon its internal algorithm and event loop engine design, node will select one "tick" of JavaScript to execute, run it, then repeat the process all over again. That's what is meant by the event loop. Node is basically at all times determining "what's the next little bit of JavaScript I should run?", then running it. This factors in which IO the OS has completed, and things that have been queued up in JavaScript via calls to setTimeout or process.nextTick.
2: If these setTimeout will get executed behind the scenes while more requests are coming and in and being executed, the thing carry out the async executions behind the scenes is that the one we are talking about EventLoop?
No JavaScript gets executed behind the scenes. All the JavaScript in your program runs front and center, one at a time. What happens behind the scenes is the OS handles IO and node waits for that to be ready and node manages its queue of javascript waiting to execute.
3: How can JS Engine know if it is an async function so that it can put it in the EventLoop?
There is a fixed set of functions in node core that are async because they make system calls and node knows which these are because they have to call the OS or C++. Basically all network and filesystem IO as well as child process interactions will be asynchronous and the ONLY way JavaScript can get node to run something asynchronously is by invoking one of the async functions provided by the node core library. Even if you are using an npm package that defines it's own API, in order to yield the event loop, eventually that npm package's code will call one of node core's async functions and that's when node knows the tick is complete and it can start the event loop algorithm again.
4 The Event Loop is a queue of callback functions. When an async function executes, the callback function is pushed into the queue. The JavaScript engine doesn't start processing the event loop until the code after an async function has executed.
Yes, this is true, but it's misleading. The key thing is the normal pattern is:
//Let's say this code is running in tick 1
fs.readFile("/home/barney/colors.txt", function (error, data) {
//The code inside this callback function will absolutely NOT run in tick 1
//It will run in some tick >= 2
});
//This code will absolutely also run in tick 1
//HOWEVER, typically there's not much else to do here,
//so at some point soon after queueing up some async IO, this tick
//will have nothing useful to do so it will just end because the IO result
//is necessary before anything useful can be done
So yes, you could totally block the event loop by just counting Fibonacci numbers synchronously all in memory all in the same tick, and yes that would totally freeze up your program. It's cooperative concurrency. Every tick of JavaScript must yield the event loop within some reasonable amount of time or the overall architecture fails.
Don't think the host process to be single-threaded, they are not. What is single-threaded is the portion of the host process that execute your javascript code.
Except for background workers, but these complicate the scenario...
So, all your js code run in the same thread, and there's no possibility that you get two different portions of your js code to run concurrently (so, you get not concurrency nigthmare to manage).
The js code that is executing is the last code that the host process picked up from the event loop.
In your code you can basically do two things: run synchronous instructions, and schedule functions to be executed in future, when some events happens.
Here is my mental representation (beware: it's just that, I don't know the browser implementation details!) of your example code:
console.clear(); //exec sync
console.log("a"); //exec sync
setTimeout( //schedule inAWhile to be executed at now +1 s
function inAWhile(){
console.log("b");
},1000);
console.log("c"); //exec sync
setTimeout(
function justNow(){ //schedule justNow to be executed just now
console.log("d");
},0);
While your code is running, another thread in the host process keep track of all system events that are occurring (clicks on UI, files read, networks packets received etc.)
When your code completes, it is removed from the event loop, and the host process return to checking it, to see if there are more code to run. The event loop contains two event handler more: one to be executed now (the justNow function), and another within a second (the inAWhile function).
The host process now try to match all events happened to see if there handlers registered for them.
It found that the event that justNow is waiting for has happened, so it start to run its code. When justNow function exit, it check the event loop another time, searhcing for handlers on events. Supposing that 1 s has passed, it run the inAWhile function, and so on....
The Event Loop has one simple job - to monitor the Call Stack, the Callback Queue and Micro task queue. If the Call Stack is empty, the Event Loop will take the first event from the micro task queue then from the callback queue and will push it to the Call Stack, which effectively runs it. Such an iteration is called a tick in the Event Loop.
As most developers know, that Javascript is single threaded, means two statements in javascript can not be executed in parallel which is correct. Execution happens line by line, which means each javascript statements are synchronous and blocking. But there is a way to run your code asynchronously, if you use setTimeout() function, a Web API given by the browser, which makes sure that your code executes after specified time (in millisecond).
Example:
console.log("Start");
setTimeout(function cbT(){
console.log("Set time out");
},5000);
fetch("http://developerstips.com/").then(function cbF(){
console.log("Call back from developerstips");
});
// Millions of line code
// for example it will take 10000 millisecond to execute
console.log("End");
setTimeout takes a callback function as first parameter, and time in millisecond as second parameter.
After the execution of above statement in browser console it will print
Start
End
Call back from developerstips
Set time out
Note: Your asynchronous code runs after all the synchronous code is done executing.
Understand How the code execution line by line
JS engine execute the 1st line and will print "Start" in console
In the 2nd line it sees the setTimeout function named cbT, and JS engine pushes the cbT function to callBack queue.
After this the pointer will directly jump to line no.7 and there it will see promise and JS engine push the cbF function to microtask queue.
Then it will execute Millions of line code and end it will print "End"
After the main thread end of execution the event loop will first check the micro task queue and then call back queue. In our case it takes cbF function from the micro task queue and pushes it into the call stack then it will pick cbT funcion from the call back queue and push into the call stack.
JavaScript is high-level, single-threaded language, interpreted language. This means that it needs an interpreter which converts the JS code to a machine code. interpreter means engine. V8 engines for chrome and webkit for safari. Every engine contains memory, call stack, event loop, timer, web API, events, etc.
Event loop: microtasks and macrotasks
The event loop concept is very simple. There’s an endless loop, where the JavaScript engine waits for tasks, executes them and then sleeps, waiting for more tasks
Tasks are set – the engine handles them – then waits for more tasks (while sleeping and consuming close to zero CPU). It may happen that a task comes while the engine is busy, then it’s enqueued. The tasks form a queue, so-called “macrotask queue”
Microtasks come solely from our code. They are usually created by promises: an execution of .then/catch/finally handler becomes a microtask. Microtasks are used “under the cover” of await as well, as it’s another form of promise handling. Immediately after every macrotask, the engine executes all tasks from microtask queue, prior to running any other macrotasks or rendering or anything else.