If you have been using Javascript for a while then you may come across the phrase that it’s a single threaded language.
What does that means?
Javascript engine runs on a V8 engine that has a memory heap and a call stack.
Source of image: https://blog.sessionstack.com/how-javascript-works-event-loop-and-the-rise-of-async-programming-5-ways-to-better-coding-with-2f077c4438b5
JS is a single threaded which means only one statement is executed at a time.
Before we dive into what it means by it runs on single thread. I would want to first go over the terminology that will help you in understanding.
My attempt will be to explain in the simplest manner possible. To
understand this better you need to know a Data Structure known as Stack (Last In, First Out).
Synchronous (or sync) execution usually refers to code executing in sequence. In sync programming, the program is executed line by line, one line at a time. Each time a function is called, the program execution waits until that function returns before continuing to the next line of code.
To put it in example, you are calling someone and you’re waiting for them
to pick up so that you can talk to them. You’re not doing any other thing until they pick up the phone.
You fulfill the request sequentially.
const one() => {
const two() => {
console.log('5');
}
two();
}
So, what happens under the call stack?
The call stack job is to fill in the instructions and pop an instruction as it gets executed.
Javascriptis a single threaded language that can be non-blocking. Single threaded means it has only one call stack. Whatever is on the top of the call stack is run first.
In the above program, functions are run sequentially.
What if we have a function that is require to do heavy lifting. Should we let the user wait till that process is over?
const one() {
console.log("Hello");
}
const two () {
for(i=0; i<= 100000000000000000000000; i++){
}
const three(){
console.log("World");
}
one();
two();
three();
Consider the above example, what if our second function has to loop through huge numbers. Does this means three() has to wait till two() is executed.
Technically, Yes!
In our small example it may not mean much but if we have to implement in a real project then the users may not be able to do anything until the first process is finished.
Asynchronous (or async) execution refers to execution that doesn’t run in the sequence it appears in the code. In async programming the program
doesn’t wait for the task to complete and can move on to the next task.
To put it in example: You call someone and while you’re waiting for them pick up the phone, you’re also running errands.
Different languages have different ways to implement asynchronous. The most popular is through Multi-threading.
In brief, Java implements multi-threading by creating a child thread which
does it’s own separate execution and then merges back with the parent
thread.
This however can run into a problem known as Deadlock, which can be dealt with various deadlock prevention mechanism.
Since, we are concerned about implementing asynchronous in Javascript. Let’s see how we can do it.
Try running this in console and see what happens.
console.log('1');
setTimeout(()=> {
console.log('2')}, 3000);
console.log('3');
You may see 1 3 and with a brief delay 2 shows up. Why is this happening?
In a nutshell, the asynchronous implementation in Javascript is done through a call stack, call back queue and Web API and event loop.
Call stack job as we seen earlier is to check what instruction is at the top of the stack and execute it.
The job of event loop is to continuously check if an event has occurred, like
mouse click or keyboard stroke so that it can send that to call stack. Of course, you want your mouse click to be given higher priority for execution than an image load.
In Javascript, All instructions are put on a call stack. When the execution pointer arrives at setTimeout, the engine sees it as a Web API instruction and pops it out and sends it to Web API, and the call stack will continue to execute the remaining instructions. Once the Web API is done with the
execution, it will arrive at the call back queue.
The engine checks if the call stack is empty. If it is empty, then we check callback queue in our case we have the response of setTimeout sitting in it. The callback queue sends it to call back stack and the instruction is executed.
The other way to think about this is when you make an API request. Say for
example your website needs to fetch an image from a server. Should your
website refuse to load other parts till the image arrives? That would be a bad user experience.
When call stack sees it needs to fetch an image, it pops and send it to Web API and continues executing the remaining functions.
The response of image request is stored in call stack queue.
When the call stack is empty, the event loop which is continuously running
looks over the Call stack queue if it has anything. If it does, in our case the response of image request. It puts that on the call stack and execute the instruction.
The benefit of this procedure is that JavaScript does not need to worry about how many cores or nodes a CPU is running on. There is only single call stack for this implementation.