We know Javascript is single-threaded and synchronous. Read more about this here.
Single-Threaded = Process one line at a time.
Synchronous = Sequence = one after other.
Let's give Rashomon's effect on synchronicity.
If it processes one after the other, the second line waits for the first line and the third line for the second one in the code, and so on.
If any line or block takes more time, the following lines or blocks will have to wait till the former gets completed.
But we may have some lines in the code whose completion time is not controlled by javascript execution.
If those uncontrolled ones take 'x' seconds to complete, the following lines or blocks should wait for 'x' seconds. So synchronous is
I don't know how much time it will take to complete the process. Please wait for this arbitrary time for your execution.
This is the limitation of a synchronous system or execution. We can't afford the uncontrolled time of a particular block or code to execute the rest of the code.
This is the evolution point of Asynchronous programming.
So Asynchronous is
I don't know how much time it will take to complete the process. Don't hold and stop your execution for me.
Asynchronocity arises majorly from API calls and timeouts. We will see this later.
For example,
const a = 1;
let b = 2;
// After 3 seconds,it will run
setTimeout(() => {
console.log(a)
console.log(b)
},3000);
console.log('Hello')
b = 3;
When you run the above code, the output will be
Hello
// After 3 seconds,
1
3
When JS identifies the setTimeout function, the timeout is run for 3 seconds. When the timeout is finished, it puts it into a queue. Who runs the timeout? In most cases, it will be some internal mechanism on which it runs(browser engine or node js).
Remember the queue?
This happens because JS can't wait 3 seconds to execute the rest of the code. So asynchronous operations happen because of this queue.
Stack is used for synchronous execution.
The queue is used for Asynchronous execution.
Therefore it puts setTimeout in the queue and jumps to the following code. It prints 'Hello'.
Then 'b' is reassigned to 3.
After 3 seconds, the setTimeout function is pushed into the queue. Anyway, setTimeout has a callback function(anonymous) inside it.
This anonymous callback function is pushed into the call stack. A separate execution context is created for this function and hence scope.
When it reaches consoling 'a', it will print 1.
When it reaches consoling 'b', it will print 3. Wait! Why not 2?
This is because the first reassignment of b takes place, and then setTimeout takes place even though setTimeout comes before the reassignment in the code. Thanks to the asynchronicity of JS.
Who coordinates stack and queue? Wait! We will give a name to this queue - Tasks queue.
This coordinator is called the Event loop.
Stack vs. queue?


Stack = Vertical arrangement = Easy to remove top most book = Last added book = Last In First Out(LIFO)
Queue = Horizontal arrangement = Easy to remove first book = First added book = First In First Out(FIFO)

While executing a program in the call stack, say some task is added to the tasks queue(e.g., timeout is finished). What does the event loop do in this case?
The answer to this is critical! We know JS is synchronous, and then we add asynchronicity as an add-on feature.
Since synchronous is related to call-stack, only when the call-stack is empty does - The event loop looks for the tasks queue.
Tasks queue programs are run just the same as synchronous ones—the only difference between the two who control the program completion time.
The event loop only looks for the tasks queue when the call stack is empty.
Which tasks will be picked first if more tasks are in the queue? FIFO. One which arrived first in queue = Oldest task among the tasks.
Well! Asynchronous is not as simple as that.
Remember the setTimeout function, right? We are, to some extent, certain that code will run after 3 seconds, but time is not managed by javascript. Because we as a developer breaks the synchronicity by specifying the time.
Anything that breaks synchronous = Asynchronous.
Let's look at another scenario,
We need data from another application. So we send an API request to that application, and finally, data is obtained by API response.
Based on the data we obtained, we do some operations using the data, like rendering the table on the screen.
We will do some investigation here.
Both setTimeout and API calls break synchronous operation because of the time it takes to complete and hence disrupt the synchronous flow of execution. Therefore Asynchronous!
The code we will execute is predefined and discreet in the case of setTimeout, but in the case of API calls, the code depends on the data we get as a response.
We are certain of the result/output in the setTimeout function unless some error appears in the code.
The expected result/output of API code is uncertain. If we want to display it in the table, we don't know how many rows and columns before hand. These kinds of scenarios are called Promises in JS.
Promises are beyond the scope of this article, and we will have a different dedicated post for them.
As a rule of thumb, remember this.
Promises = Code dependent on the response data, and the data is from another application upon which code executes = Uncertainty in result/output.
So there are two types of asynchronous tasks - Independent and Dependent.
They need to be treated separately. If they are separated, which one needs priority? Who gives priority? again Event loop!
Which one breaks synchronicity deliberately? It is the setTimeout because we defer the execution by giving a specified time. But in the case of API calls, time is not in our hands. We need to use the data as soon as we receive the response. So we did not consciously defer the execution.
Naturally, API calls should be given priority.
How can we differentiate tasks?
There needs to be another queue and this queue is called the Micro Tasks queue. So we will rename the already established queue called Macro Tasks Queue.

Point to remember:
JS makes use of the tasks queue for asynchronous operation.
There are two kinds of asynchronous operation - Dependent(Promises) and Independent(Settimeout).
Therefore two queues - Micro and Macro tasks queue.
The micro-tasks queue is given preferences over the macro tasks queue. Micro tasks enqueue Promises-based calls(API calls).
Overall coordinator of call stack-micro tasks and macro tasks queue is called Event Loop.
The event loop only looks for any task queue when the call stack is empty.