Event Loop in JavaScript 🎡


In the world of JavaScript, the event loop is what makes asynchronous operations possible without blocking the main thread. It’s the heart of how JavaScript handles multiple tasks at once, keeping web applications smooth and responsive.
If you've ever wondered why JavaScript doesn’t freeze when waiting for a network request or a timer to complete, the event loop is the answer. Understanding this concept is key to writing efficient and performant web applications.
Now, if terms like 'asynchronous' or 'call stack' sound intimidating, don’t worry—I’ll break them down so that anyone, regardless of experience level, can grasp how the event loop works. But before we delve into what that means, let’s first understand how JavaScript works and why we even need the event loop.
JavaScript Works on a Single Thread
Imagine a single waiter at a restaurant taking orders, serving food, and handling payments. Since there is only one waiter (just like JavaScript is single-threaded), they can only serve one customer at a time. Each order must be taken, prepared, and served before moving on to the next one.
console.log('Welcome to the restaurant');
function takeOrder(order1, order2) {
return `Order placed: ${order1} and ${order2}`;
}
const order = takeOrder('Pasta', 'Pizza');
console.log(order);
console.log('Serve the food');
// Expected Output:
// Welcome to the restaurant
// Order placed: Pasta and Pizza
// Serve the food
Explanation:
The waiter welcomes the customer.
The
takeOrder
function is called, and the order is placed immediately.The food is served.
Each step happens in order, just like JavaScript executes code sequentially.
Handling Delays with Asynchronous Code
Now, what if a dish takes time to cook? Instead of making all customers wait, the waiter moves on to take the next order while the kitchen prepares the dish in the background.
console.log('Welcome to the restaurant');
setTimeout(() => {
console.log('Dish is ready!');
}, 5000);
console.log('Take the next order');
// Welcome to the restaurant
// Take the next order
// Dish is ready!
Explanation:
The waiter welcomes the customer.
The dish is sent to the kitchen (
setTimeout
schedules an async task).Instead of waiting, the waiter takes the next order (
Take the next order
logs immediately).Once the dish is ready (after 5 seconds), the waiter serves it (
Dish is ready!
).
Why Do We Need the Event Loop?
If the waiter stood still waiting for the dish to be ready, the entire restaurant would come to a halt. That’s exactly what would happen in JavaScript without the event loop—long-running operations would freeze everything else.
Instead of waiting 5 seconds and blocking other operations, the event loop allows JavaScript to move on and handle other tasks while waiting for the dish (async operation) to be prepared.
What is the Event Loop?
The event loop is a process that enables JavaScript to handle asynchronous operations while maintaining a non-blocking environment, even though it runs on a single thread. It continuously checks the call stack and task queues to decide what should be executed next, allowing asynchronous operations like timers, API calls, and user interactions to run without blocking the main thread.
Call Stack
The call stack is a data structure that keeps track of function calls in JavaScript. It follows the Last In, First Out (LIFO) principle, meaning the last function pushed onto the stack is the first to be executed.Web APIs
JavaScript’s runtime environment (browser or Node.js) provides Web APIs, such as:
setTimeout()
fetch()
DOM Events
setInterval()
AJAX requests
These APIs allow JavaScript to perform asynchronous operations by handling tasks in the background.
Task Queue (Callback Queue)
When an asynchronous function likesetTimeout()
completes, its callback is placed in the task queue. These tasks wait for the call stack to be empty before they run.Microtask Queue (Higher Priority)
Microtasks like Promises go into a separate microtask queue. These tasks always run before the tasks in the task queue.
How the Event Loop Works?
console.log("Start");
setTimeout(() => {
console.log("Task Queue: setTimeout");
}, 2);
Promise.resolve().then(() => {
console.log("Microtask Queue: Promise");
});
console.log("End");
The JavaScript engine starts executing the script line by line in the Call Stack.
"Start" is printed.
setTimeout() is encountered
The browser sets a 2-second timer and registers the callback function (
() => console.log("Task Queue: setTimeout")
).The function does not execute now; instead, it is placed in the Task Queue to run later.
setTimeout
itself exits, and execution moves forward.
Promise.resolve().then(...) is encountered
The callback inside
.then()
(console.log("Microtask Queue: Promise")
) is added to the Microtask Queue.This function will execute after all synchronous code finishes.
"End" is printed immediately.
Call Stack is now empty. Since all synchronous tasks are done, the Event Loop checks the Microtask Queue first.
"Microtask Queue: Promise" is printed from the .then() callback.
2 seconds pass, timer expires. The browser moves the setTimeout callback function to the Task Queue.
Event Loop checks the Call Stack. Since the Call Stack is empty, it takes the setTimeout callback from the Task Queue and runs it.
Task Queue executes setTimeout callback. "Task Queue: setTimeout" is printed.
Even though the delay is 0ms, setTimeout does not execute immediately; it waits until the Call Stack is empty and after all Microtasks are completed.
If the setTimeout delay is 0 milliseconds, the execution flow remains the same because setTimeout always places its callback in the Task Queue, which runs after the Microtask Queue. So, even with 0ms, JavaScript first runs all synchronous code, then executes Promises (Microtask Queue), and only after that, it picks up the setTimeout callback from the Task Queue. This means the output will still be:
Start
End
Microtask Queue: Promise
Task Queue: setTimeout
Task Starvation
Now imagine this, if micro tasks keep popping up without allowing other tasks a chance to run, what happens next ?
In this scenario, the callback queue won’t get the opportunity to execute it’s tasks. This is Startvation of Tasks in the callback queue.
❤️ If you found this helpful, consider sharing it with fellow developers!
💡 Still have questions or insights? Drop them in the comments—I’d love to discuss!
Subscribe to my newsletter
Read articles from Manshi Tyagi directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Manshi Tyagi
Manshi Tyagi
Hi, I'm Manshi, a full-stack web developer. I write about building web apps and my journey in tech. Follow along as I share what I'm learning and creating.