When I first started to learn Javascript in depth, this is the description of Javascript that I stumbled upon:
A single threaded non-blocking asynchronous programming language.
In this article, I would like to attempt to explain the above description of Javascript.
Based on the description above, if Javascript is single threaded, meaning it could only perform one task at a time, how could it be non-blocking when performing asynchronous tasks - tasks that takes longer time to complete, without blocking the execution of the main thread? Mustn't Javascript be doing multithreading if it were to perform such asynchronous tasks?
Let's look at the example below and see whether Javascript is really single threaded like how it claims to be:
function getData() {
setTimeout(function print() => {
console.log("hello");
}, 1000);
}
function main() {
getData();
console.log("world");
}
main();
// output1:
// hello
// world
// OR
// output2:
// world
// hello
Based on the code snippet above, try to guess which output the program would produce - output1
or output2
?
Most of us would easily guess it is output2
and yes, it is correct. From an amateur programmer point of view, output2
makes perfect sense, because getData
takes 1 second to resolve, hence the program would log world
followed by hello
once the 1 second has passed. So what is the catch here? Well, the catch is that Javascript is single threaded!
If Javascript is really single threaded, like how it claims to be, it would have produced output1
instead of output2
. This is because, for single threaded languages, it would need to wait for the getData
to be resolved first and would block the execution of the subsequent lines, hence logging hello
followed by world
.
But as you can see above, this wasn't the case because calling getData
did not block the execution of the subsequent lines (this is what they mean when they say Javascript is non-blocking) and the program logged world
first and once getData
got resolved after sometime (this is what they mean by asynchronous - something that takes longer time to complete), it then logged hello
.
So, is Javascript really single threaded like how it claims to be? The answer is yes. But, how does Javascript achieves this non-blocking asynchronous nature while being single threaded?
You see, although Javascript itself is a single threaded language, Javascript engines such as the V8 engine provides components such as the call stack, callback queue, event loop and makes use of the web APIs provided by the web browser and augments Javascript to create its multithreading illusion.
Below are the comprehensive definition of these components:
- Call Stack - a stack data structure that keeps track of where in the program we are. While the Javascript engine interprets the code, when it runs into a function, it would be put on the stack and when a function is returned, the top of the stack would be popped off.
- Web APIs - a set of APIs such as the setTimout, fetchAPI and DOM Event Listeners APIs provided by the browser which would be consumed by the Javascript engine. These APIs would run on a separate thread in the browser. A Javascript callback would be required to be provided in order to call these APIs.
- Callback Queue - a queue data structure that keeps track of the callbacks. When any of the calls to the Web API completes, it's callback function would be pushed into the Callback Queue.
- Event Loop - an observer that looks at the Call Stack and Callback Queue. Whenever the Call Stack is empty, it would pop the first item from the Callback Queue and pushes it into the Call Stack. This process would run forever in loop.
In regards to the above program, the following is what happens under the hood:
- Function call
main()
gets pushed into the Call Stack. - Function call
getData()
gets pushed into the Call Stack. - Function call
setTimeout()
gets pushed into the Call Stack. - A call to the Web API's
setTimeout()
function is made withprint()
as its callback and the Web API starts a timer. Once 1 second has passed, the Web API would push theprint()
callback to the Callback Queue. - Function call
setTimeout()
returns and it is popped off the Call Stack. - Function call
getData()
returns and it is popped off the Call Stack. - Function call
console.log("world")
get pushed into the Call Stack. - Function call
console.log("world")
printsworld
and returns. - Function call
console.log("world")
is popped of the Call Stack. - Function call
main()
is popped of the Call Stack. - The call stack is now empty, the Event Loop looks at the Callback Queue to see if there are any callbacks present to be pushed into the Call Stack.
- If 1 seconds had already passed by now, the
print()
callback would have been passed to the Callback Queue. The event loop pops theprint()
callback off the Callback Queue and pushes it into the Call Stack. - Function
console.log("hello")
get pushed into the Call Stack. - Function
console.log("hello")
printshello
and returns. - Function
console.log("hello")
is popped of the Call Stack. - Function
print()
is popped of the Call Stack.
Below is the visualisation of the process mentioned above and this visualisation can be found here.
As a conclusion, I think we have made justice that Javascript is a single threaded non-blocking asynchronous programming language. Javascript engines, such as the V8 engine augments Javascript and creates an illusion that it is multithreaded using various components such as the call stack, callback queue, event loop and web APIs.
I would like to end this article with a quiz to test our understanding of Javascript internals, please free to answer them in the comments below.
// What would be the output when main is called?
// Wiil it output output1 or output2?
function main() {
console.log("1");
setTimeout(function callback() {
console.log("2");
}, 0);
console.log("3");
}
main();
// output1:
// 1
// 2
// 3
// output2:
// 1
// 3
// 2
Reference:
Top comments (2)
is the answer output2? great article by the way! 😁
thank you! and yes the answer is output2.