Wait, what’s this? A bookish definition? 🤔
Let’s break it down!
The keywords here as regards this article are single-threaded, non-blocking, concurrent, and asynchronous.
A thread of execution is the smallest sequence of programmed instruction that can be managed independently by a scheduler. A programming language is single-threaded means it can only perform one task or operation at a single time. This means it would execute an entire process from start to end without the thread being interrupted or stopped.
Unlike multi-threaded languages where multiple processes can be run on several threads concurrently without blocking each other.
But what does blocking mean?
There is no one definition of blocking; it just simply means things that are running slowly on the thread. So non-blocking means things that aren’t slow on the thread.
Relax! We’d find out in a bit 😉.
Concurrency means that the code is being executed concurrently by more than one thread.
Asynchronous programming means that the code runs in an event loop. When there is a blocking operation, the event is started. The blocking code keeps running without blocking the main execution thread. When the blocking code finishes running, it queue’s the result of the blocking operations and pushes them back to the stack.
Before we proceed, let’s have a recap of the above.
But the above doesn’t exactly add up, how can a single-threaded language be non-blocking, concurrent, and asynchronous?
The V8 does two major things;
- Heap memory allocation
- Call stack execution context
Sadly, our suspicion was wrong. The V8 has just one call stack, think of the call stack as the thread.
One thread === one call stack === one execution at a time.
Let’s try to find out by writing a simple yet common asynchronous code and analyze it together.
Some other thread seems to have helped us execute that timeout since we are pretty sure a thread can only execute one single task at any point in time.
Let’s take a sneak peek into the V8 Source Code for a while.
Wait… what??!!! There are no timer functions in V8, no DOM? No events? No AJAX?…. Yeeeeessss!!!
The Call Stack
A stack is a data structure in which the last element added is always the first to be removed from the stack, you could think of it as a stack of a plate in which only the first plate which was the last added can be removed first. A Call Stack is simply nothing but a stack data structure where tasks or code is being executed accordingly.
Let’s consider the below example;
When you call the function
printSquare() , it is pushed onto the call stack, the
printSquare() function calls the square() function. The
square() function is pushed onto the stack and also calls the
multiply() function. The multiply function is pushed onto the stack. Since the multiply function returns and is the last thing that was pushed to the stack, its get resolved first and is removed from the stack, followed by the
square() function and then the
The Web API
This is where code that isn’t handled by the V8 engine is executed to not “block” the main execution thread. When the Call Stack encounters a web API function, the process is immediately handed over to the Web API, where it is being executed and freeing the Call Stack to perform other operations during its execution.
Let’s go back to our
setTimeout example above;
When we run the code, the first console.log line gets pushed to the stack and we get our output almost immediately, on getting to the timeout, timers are handled by browser and aren’t a part of V8’s core implementation, it’s get pushed to the Web API instead, freeing the stack so it could perform other operations.
While the timeout is still running, the stack goes ahead to the next line of action and runs the last console.log, which explains why we get that outputted before the timer output. Once the timer is completed, something happens. The console.log in then timer magically appears in the call stack again!
The Event Loop
Before we discuss the event loop, lets first go through the function of the task queue.
Back to our timeout example, once the Web API finishes executing the task, it doesn’t just push it back to the Call Stack automatically. It goes to the Task Queue.
A queue is a data structure that works on the First in First out principle, so as tasks get pushed into the queue, they get out in that same order. Tasks that have been executed by the Web API’s, which are being pushed to the Task Queue, then go back to the Call Stack to get their result printed out.
But wait. WHAT THE HECK IS THE EVENT LOOP???
The event loop is a process that waits for the Call Stack to be clear before pushing callbacks from the Task Queue to the Call Stack. Once the Stack is clear, the event loop triggers and checks the Task Queue for available callbacks. If there are any, it pushes it to the Call Stack, waits for the Call Stack to be clear again, and repeats the same process.
The above diagram demonstrates the basic workflow between the Event Loop and the Task Queue.