A management logic behind the pseudo asynchronous nature of javascript. The core components are as follows :
- Call Stack
- Callback Queue (Macrotask)
- Web APIs
- Microtask Queue
Call Stack
The main component which is basically a LIFO stack and popping the item in this stack means the task got executed. It contains execution context of its items, can be a function or a statement.
The first item that goes in there when we execute a javascript file is a Global execution context. Imagine the context of the entire file being there, the global variables and functions. then it executes the lines in the file one by one.
Callback Queue
Also known as macro task queue. This contains all the callbacks that have been passed in a function as a parameter. Assume if the call stack executes a function which further executes a callback inside it. the callback stuff will go in the callback queue, which is also a LIFO queue. Not all callbacks are stored in callback queue, a few go into micro task as well.
Microtask Queue
Similar to the callback queue but has higher priority, consists of .then, .finally, .catch and other promise based callbacks in it. The event loop makes sure if the call stack is empty, first we send the micro tasks to be executed and then anything else. If there is no micro task, then it sends a macro task and checks again if any new micro task has been added to the queue.
Web APIs
Its and environment provided by wherever you are running the code either node or browser, where it has a set of utilities thats not possible by the other structures inside event loop like timeout, fetching data from an API and other stuff. The execution of such tasks are stored inside the web APIs. Its not a stack or queue, just a set of tools that does things for you. Web API execution might also have callbacks and micro task callbacks, so they will still go to their specific queues.
Example :
console.log("Start");
setTimeout(() => console.log("Macrotask (setTimeout)"), 0);
Promise.resolve() .then(() => console.log("Microtask 1")) .then(() => console.log("Microtask 2"));
fetch("https://jsonplaceholder.typicode.com/todos/1") .then(() => console.log("Microtask (fetch.then)"));
console.log("End");Now let your brain build all the pillars we just read about and let each line flow through them.
- After global exec. context, we send
console.log("Start")in the call stack and it gets executed immediately there. - 1 - We have a web API called setTimeout there, so that goes into web apis and we move further.
- Now we have a resolved promise that sends the promise based callbacks into the micro task queue.
- Fetch goes inside the web apis again and is still being executed.
- Remember until now there’s nothing in the call stack yet, and everything is being transited to their respective pillar of event loop.
- Next is
console.log("End"), which is a simple statement so it goes inside call stack and is executed immediately again. - 2 - Now for the rest, remember the priority thing i told you about, so since the promise line too takes no time, it will get executed before setTimeout line (priority) and we’ll have the chained micro task logged. - 3
- After this, the micro task queue is empty (since the fetch is slow and takes time to get the data). so the event loop executes the macro task - i.e. setTimeout callback. - 4
- Finally after data is fetched the fetch log will be executed - 5
So, the output will look like :
StartEndMicrotask 1Microtask 2Macrotask (setTimeout)Microtask (fetch.then)