Published on

Understanding the JavaScript Event Loop

Authors
  • avatar
    Name
    Hieu Cao
    Twitter

Introduction

The JavaScript event loop is a crucial concept for understanding how asynchronous programming works in JavaScript. It is responsible for handling the execution of multiple operations, enabling JavaScript to perform non-blocking tasks despite being single-threaded.

In this blog post, we'll explore the event loop, its components, and how it manages asynchronous operations.

The Call Stack

The call stack is a fundamental part of the JavaScript runtime. It keeps track of function calls and ensures that they execute in the correct order. Functions are pushed onto the stack when invoked and popped off once they complete.

Example:

function greet() {
  console.log('Hello, world!')
}

greet()

In this example, the greet function is added to the stack, executed, and then removed.

The Web APIs/Node APIs

In browsers or Node.js environments, there are additional APIs that allow for asynchronous tasks like HTTP requests, timers, or file system operations. These APIs operate independently of the main JavaScript thread.

Example:

console.log('Start')

setTimeout(() => {
  console.log('Timeout')
}, 1000)

console.log('End')

Here’s what happens:

  1. console.log("Start") is executed and logged immediately.
  2. setTimeout is called, and the timer is started by the Web API but doesn’t block the call stack.
  3. console.log("End") is executed immediately.
  4. After 1000ms, the callback function is placed in the task queue: console.log('Timeout')

The Task Queue

The task queue is where callback functions from asynchronous operations are queued for execution. These tasks wait for the call stack to be empty before being executed.

The Event Loop

The event loop continuously monitors the call stack and the task queue. If the call stack is empty, the event loop pushes the next task from the queue onto the stack for execution.

Visualizing the Process:

  1. Code execution begins in the call stack.
  2. Asynchronous operations are sent to the Web API.
  3. Once completed, their callbacks are placed in the task queue.
  4. The event loop checks if the stack is empty and executes tasks from the queue.

Microtasks vs. Macrotasks

Tasks are categorized into microtasks and macrotasks:

  • Microtasks: Include promises and MutationObserver callbacks.
  • Macrotasks: Include setTimeout, setInterval, and I/O tasks.

Order of Execution:

Microtasks have higher priority than macrotasks. After each task from the call stack, all queued microtasks are executed before moving to macrotasks.

Example:

console.log('Start')

setTimeout(() => console.log('Timeout'), 0)

Promise.resolve().then(() => console.log('Promise'))

console.log('End')

Output:

Start
End
Promise
Timeout

Explain: event loop js

Common Pitfalls

Understanding the event loop can help avoid common mistakes, such as:

  1. Assuming Immediate Execution: Callbacks from setTimeout or setInterval are not guaranteed to execute precisely after the specified delay.
  2. Blocking the Event Loop: Long-running synchronous code blocks the event loop, delaying the execution of asynchronous tasks.

Example:

setTimeout(() => console.log('Timeout'), 0)

while (true) {
  // Infinite loop blocks the event loop.
}

Conclusion

The JavaScript event loop is an essential mechanism for enabling asynchronous programming in a single-threaded environment. By understanding how the call stack, task queue, and event loop interact, you can write more efficient and non-blocking code.

Happy coding!