Most of the code is synchronous.
It is executed in a line-by-line fashion, i.e., each line of code waits before the previous line to finish its execution.
Long-running code operations block the code execution for further stacked code executions.
Asynchronous code isn’t synchronous. I.e., the code is executed after a task that runs in the background finishes.
It is non-blocking in nature. Execution doesn’t wait for an asynchronous task to finish its work.
Callback functions alone don’t make the code asynchronous.
Under this approach, the tasks run and complete in an interleaved fashion. I.e., the tasks run concurrently but at a given point of time, only one task is being executed. This happens when the tasks are braked into small parts and are managed pretty well. This is also shown in the figure below.
In contrast, under the approach of parallelism, we can the tasks run simultaneously, i.e., at a particular point of time many tasks can run regardless of other tasks running. This happens when we multithread the tasks into different threads available for the interpreter.
It is the place where all the objects and data are stored. This is similar to the heap storage we see on various other languages like C++, Java, etc. It contains the store of the data related to all the objects, arrays, etc. that we create in the code.
This is the place where asynchronous code is queued before passing to the call stack. The passing of the code task from the callback queue to the call stack is taken care of by the event loop. In addition to this, there is also a micro tasks queue.
The microtasks queue is similar to the callback queue but has a higher priority of execution than it. In other words, if there is a situation where the call stack is empty (except the global execution context ) and there are two tasks to be executed, one from the micro-tasks queue and the other from the normal task queue or callback queue, then the code task present in the microtask queue has the higher priority than the latter.
Having understood the basic terminologies involved, let’s quickly understand how the asynchronous code work.
Here, we get introduced to the concept of the event loop. In simple words, an event loop can be defined as a smart technique of execution of executing the code from the callback queue by passing into the call stack, once it is found to be empty ( Except the global execution context ).
The event loop decides when to execute each code task present in the callback queue and the micro-tasks queue. Let us understand the execution process of all the code in an imaginary situation. Let us try to generalize the process into different steps :
All the code tasks present in the call stack are executed in an orderly fashion. It is synchronous and waits for the previous code task to be executed. In this step, all the code tasks in the call stack are executed.
Once the asynchronous task finishes getting loaded in the background, it is sent to the callback queue. The callback function attached to this asynchronous task is waiting to be executed right here. This asynchronous is then queued up to be executed in the callback queue.
Now, the part of event loops comes into play. The event loop continuously checks if the call stack is empty and once it finds it to be empty, it takes the first task in the callback queue and stacks it into the call stack which is then executed. This process continues until the event loop finds the call stack and callback queue to be empty.
No, let us understand how they work behind the scenes. Promises are also a special type of asynchronous tasks which once after loading get queued up in a special place called micro tasks queue. This microtasks queue has higher priority as compared to the callback queue when execution. The event loop also checks for the tasks in the micro-tasks queue when checking for tasks to be executed in the callback queue. If it finds any task to be executed, then it gives the micro-tasks higher priority and they are executed first.
Let us consider the following example. In this case, there are two synchronous and two asynchronous tasks ( Read comments ). In this example, first, synchronous task 1 is sent to the callback and is executed. Then, the asynchronous task 1 is loaded in the background which is a built promise. Then, asynchronous task 2 is loaded in the background. The last synchronous task is executed asap. Then the promise is sent to the micro tasks queue, at the same time setTimeout which is an asynchronous task is loaded behind. Now, we come across a clash between asynchronous task 1 and asynchronous task 2. As the promise is sent to the micro tasks queue, it has higher priority and is sent to the call stack and is executed. Then the setTimeout is executed. Here we can see that due to the already queued up tasks, the setTimeout is delayed and the callback is executed after more than 0 seconds(the timer set).
//Synchronous task no 1 console.log("This is executed first"); //Asynchronous task no 1 Promise.resolve("This is executed third") .then((res)=>console.log(res)); //Asynchronous task no 1 setTimeout(()=>console.log("This is executed fourth"),0); //Synchronous task no 2 console.log("This is executed second");
Thank you for reading my article. Meet you in the next article friends. This article would be continued further. So please follow me and stay connected. If you found this article useful, please let me know your feedback in the comments below. Also A reaction would always be appreciated.
Having a cheat sheet for strings is so helpful for you, so please check out this wonderful thread ⬇️
#DEVCommunity #webdev #Web307:12 AM - 15 Dec 2021