JavaScript runs one thing at a time on one call stack. Slow work (timers, network) is handed to the browser, and when it finishes, its callback waits in a queue.
The event loop is the doorman: whenever the call stack is empty, it takes the next callback from the queue and pushes it on. That single rule explains why a setTimeout(0) still waits for your code to finish, and why one blocking loop freezes a whole page.