"JavaScript is a single-threaded and non-blocking language". Chances are you have heard that statement a couple of times and have no idea what it means or you don't fully understand it.
If you have not heard of it before, well now you know. But you might ask "What does that even me", well in this article am going to try to explain what it means in the simplest possible form.
Now, why should you or any JavaScript developer even care about understanding what that statement means? This is because the statement explains how the JavaScript runtime works behind the hood. It explains how JavaScript executes the code we write.
Understanding that statement alone will help you write better code, and also help you know when to write code synchronously or asynchronously.
Alright, let's get into it already. I will explain this by breaking it down into two subheadings:
- What is a single-threaded language
- How is JavaScript non-blocking
What is a single-threaded language
A single-threaded language is a language that uses only one thread. In other words, it executes one thing at a time. JavaScript is single-threaded, so it executes the current line of code before moving to the next.
Think of it like a basic queue of people waiting to withdraw their money from an ATM, no matter how smart the ATM is, it can only serve one customer at a time, right?
Let's see an example in code
function funcOne() {
console.log('First function');
}
function funcTwo() {
console.log('Second function');
}
funcOne();
funcTwo();
From the above example, JavaScript executes the function as called one after the other. A function that isn't invoked will not be executed, but it will be parsed - a way of JavaScript knowing that there is a function there so when called it can easily go back to it.
Now, each function is pushed to the call stack to get executed. The call stack keeps track of the current functions that are being run and what functions are called within the function. When the current function is finished, the call stack pushes it out and continues from where it stopped in the code.
So while the funcOne
function and its children are being executed, every other function (funcTwo
) will have to wait. This is how single-threaded language works and this kind of behavior may give rise to blocking.
Basically, any code that is slow to execute is blocking. It could be a network request, loading an image, a loop that runs to 1 million, etc. Other interesting situations could be an infinite loop, or an alert message waiting for a response from the user. For example:
alert('Hello World');
console.log('You received a hello world message!');
The console.log
function is blocked from executing because of the alert
function. When you run it in the browser you will get an alert message Hello World
, not until there is a response to the alert message every other function (including buttons) in the browser will be blocked from executing.
Another common example would be an infinite loop.
while (true) {
console.log('I am true');
}
console.log('I will never be executed');
If you execute the code above in a browser (which you shouldn't) you would notice that the buttons on the website (if there are any) wouldn't seem to be working, selecting a text (if there are any) would have no effect. Technically the website is blocked. And there is no way "I will never be executed" is logging in the console.
So technically we're saying every single-threaded language (including JavaScript) is blocking.
How is JavaScript non-blocking
So far we've learned about how JavaScript executes its codes naturally. We've also seen that as a result of JavaScript being single-threaded in nature, some codes in JavaScript can be blocking. But if you haven't fully grasped what this blocking means, here is a metaphor to give you a light understanding before we go a little deep:
Imagine a cashier in the bank who waits for a customer to correct his/her errors while other customers in the queue wait. So not until the current customer is done with his/her correction(s) the cashier will not attend to any other customers. This is how JavaScript works - it is synchronous in nature.
But you and I know there is a simple solution to this problem, which is:
While the current customer corrects his/her error, he/she can move to the side so other customers that are ready can be attended to. Whenever the initial customer is done, he/she can come back to the cashier to do whatever.
This my friend is how JavaScript is non-blocking that is working asynchronously.
Asynchronous JavaScript
An asynchronous function will not block the execution of other functions in the call stack. For example
console.log('Hey,');
setTimeout(() => {
console.log('I am John');
}, 1000);
console.log('Bye now');
This will simply log Hey, -> Bye now -> I am John
in the console. This is because asynchronous functions (e.g the setTimeout function) will not block the execution of other functions in the call stack.
From the example above, the setTimeout function is meant to delay for 1 second before the function in it is executed, but that won't stop the console.log('Bye now')
function from executing, nor will it delay it.
This is because asynchronous functions when pushed to the call stack are pushed out of the call stack to Web APIs to be executed there. Whenever the asynchronous function is ready it is pushed back in an interesting way(we will talk about this in a moment).
So our setTimeout function in the example above is pushed to Web APIs to be executed there. Now it wasn't pushed there because of the delay of 1 second, it was pushed there because it is an asynchronous function so even if we had a delay of 0 seconds the outcome would still be the same i.e
console.log('Hey,');
setTimeout(() => {
console.log('I am John');
}, 0);
console.log('Bye now');
To log Hey, -> Bye now -> I am John
in the console. So what happens after the setTimeout function has been executed in the Web API?
When the setTimeout function is executed, it isn't pushed back to the call stack immediately, rather it is pushed to a callback queue to wait for all other functions in the call stack to be executed before it is pushed back to the call stack.
So, from Call stack -> Web API -> Callback queue -> and back to the Call stack.
This my friend is called the event loop.
This is a little different from how customers who make errors in the bank are attended to. This time the browser (being the manager/cashier at the bank) is saying if you are sidelined to make corrections to your errors or add missing details, you will have to go back to the end of the queue to wait till it gets to your turn again. Not really a good experience for us humans, but the browser loves this algorithm a lot, and we developers benefit from it too.
Pushing asynchronous functions to the web APIs can also be thought of as scheduling for later. So technically, JavaScript being non-blocking doesn't mean it's multi-threading or multi-tasking, rather it means the browser can smartly schedule asynchronous tasks for later, - "later" being when it is ready.
The same thing when making network requests asynchronously, the function is pushed out of the call stack to make the request to whatever API, and when it is ready it is pushed to the callback queue.
Conclusion
So far we've learned what it means when someone says JavaScript is single-threaded and also non-blocking. And hopefully, we've grasped a few things about the event loop. I would recommend you watch the "What the heck is the event loop" YouTube video by Philip Roberts, it will give you more understanding of the event loop and you'd know how to use the Loupe website.
P.S: Sorry about the size of the gif. But trust me you'll get a better view when you play with the Loupe website.
Alright, that's it for today guys. Follow me for more articles like this, and if you find this article useful please share. Also, let me know what you think about the article in the comment section, or you can ping me on Twitter @elijahtrillionz.