No history yet

JavaScript Event Loop

JavaScript's One-Track Mind

JavaScript has a reputation for being a bit of a multitasker. It can handle user clicks, fetch data from a server, and update animations, seemingly all at once. But here's the secret: at its core, JavaScript can only do one thing at a time. It's single-threaded, meaning it has only one call stack and can execute one piece of code at a time.

So how does it juggle so many tasks? The answer is a clever mechanism called the event loop. It's the engine that allows JavaScript to handle asynchronous operations, like waiting for a network request, without grinding the entire program to a halt.

The event loop is JavaScript's strategy for managing time-consuming tasks without freezing the main thread.

The System in Action

To understand the event loop, you need to know about its key partners: the call stack, Web APIs, the microtask queue, and the task queue.

  1. Call Stack: This is where JavaScript keeps track of its work. When a function is called, it's pushed onto the stack. When it returns, it's popped off. As we've covered, this is a last-in, first-out process.
  2. Web APIs: These are features provided by the browser environment, not the JavaScript engine itself. Things like setTimeout, DOM events, and the fetch API for making network requests live here. When you call one of these, you're handing a task off to the browser to manage.
  3. Task Queue (or Callback Queue): When a Web API finishes its job (like a timer running out or data arriving), the associated callback function doesn't go straight to the call stack. It's placed in the task queue, waiting its turn.
  4. Microtask Queue: This is a special, high-priority queue. Callbacks from Promises (like .then() or .catch()) are placed here. Microtasks always get to cut in line ahead of regular tasks.

The event loop's job is simple but crucial. It continuously asks: "Is the call stack empty?"

If the call stack is not empty, it waits. If it is empty, it first checks the microtask queue. It will take every single microtask from that queue and push them, one by one, onto the call stack to be executed. It will not stop until the microtask queue is completely empty.

Only then, if the call stack is still empty, will the event loop look at the task queue. It takes the oldest task from the queue, pushes its callback onto the call stack, and the cycle repeats.

Order of Operations

This priority system has a big impact on the order your code runs. Let's look at an example that combines synchronous code, a microtask (from a Promise), and a task (from setTimeout).

console.log('Start');

setTimeout(() => {
  console.log('Timeout callback (Task)');
}, 0);

Promise.resolve().then(() => {
  console.log('Promise resolved (Microtask)');
});

console.log('End');

What do you think the output will be? Here’s the step-by-step breakdown:

StepActionCall StackMicrotask QueueTask QueueConsole
1console.log('Start') runs.log('Start')(empty)(empty)Start
2setTimeout is called. Browser starts a 0ms timer.setTimeout()(empty)(empty)...
3Timer finishes. setTimeout callback is added to the Task Queue.(empty)(empty)timeout cb...
4Promise.resolve().then() runs.Promise.then()(empty)timeout cb...
5The promise resolves immediately. The .then() callback is added to the Microtask Queue.(empty)promise cbtimeout cb...
6console.log('End') runs.log('End')promise cbtimeout cbEnd
7Main script finishes. Call stack is empty. Event loop runs.(empty)promise cbtimeout cb...
8Event loop checks microtasks first. It finds one!promise cb(empty)timeout cbPromise resolved (Microtask)
9Microtask queue is empty. Event loop checks tasks. It finds one!timeout cb(empty)(empty)Timeout callback (Task)

The final output is:

Start End Promise resolved (Microtask) Timeout callback (Task)

Even though the setTimeout was set to 0 milliseconds, its callback had to wait in the task queue. The promise's callback, being a microtask, got to go first as soon as the main script finished.

Understanding this cycle is key to writing predictable, non-blocking code in JavaScript. It’s how you can build responsive applications that can handle complex operations without freezing up.

Quiz Questions 1/5

At its core, JavaScript's execution model is described as:

Quiz Questions 2/5

When the call stack is empty, what does the event loop check and process first?