JavaScript code runs single threaded. There is just one thing happening at a time.
The call stack: The event loop continuously checks the call stack to see if there's any function that needs to run.While doing so, it adds any function call it finds to the call stack and executes each one in order.
The event loop on every iteration looks if there's something in the call stack, and executes it, until the call stack is empty.
The use case of setTimeout(() => {}), 0) is to call a function, but execute it once every other function in the code has executed.
The Message Queue: When setTimeout() is called, the Browser or Node.js start the timer. Once the timer expires, in this case immediately as we put 0 as the timeout, the callback function is put in the Message Queue.
The loop gives priority to the call stack, and it first processes everything it finds in the call stack, and once there's nothing in there, it goes to pick up things in the message queue.
ES6 Job Queue:
ECMAScript 2015 introduced the concept of the Job Queue, which is used by Promises (also introduced in ES6/ES2015). It's a way to execute the result of an async function as soon as possible, rather than being put at the end of the call stack.
Ref: https://flaviocopes.com/javascript-event-loop/