Skip to main content
Mohammad Shehadeh — home (MSH monogram, letter M filled with the Palestinian flag)

Understanding event loop in JavaScript

Published on
3 min read

JavaScript is a single-threaded, non-blocking, asynchronous language that runs natively in the browser.

Single-Threaded

The language runs only one instruction at a time. This sets it apart from multi-threaded languages that run several instructions at once.

Single-Threaded and Multi-Threaded

Non-Blocking and Asynchronous

The language does not block the next operation. The program does not wait for a previous operation to finish before moving on.

It uses callbacks, promises, and async/await to manage asynchronous tasks.

The event loop oversees the execution flow, so single-threaded JavaScript can handle multiple tasks concurrently.

Event Loop

The event loop pulls tasks out of the queue and places them onto the call stack (the function execution stack), making sure instructions don't block other code.

Web APIs

Web APIs are built into the browser. They let JavaScript manipulate documents, fetch data from the server, and more.

Callback Queue

The callback queue is a FIFO (First In, First Out) data structure: the first element added is the first one accessed.

Call Stack

The call stack is how the JavaScript interpreter tracks function calls: which function is running now, and which functions it calls.

How event loop works

When a function is invoked, it's added to the call stack. A stack is first in, last out, so the function is popped off once it returns a value.

Let's figure out what gets logged to the console:

1console.log('First log');
2
3// setTimeout delays a task without blocking the main thread
4setTimeout(() => {
5  console.log('Last log');
6}, 100);
7
8console.log('Second log');

Here's the execution order:

  1. console.log('First log') is added directly to the call stack
  2. The callback passed to setTimeout is added to the Web APIs
  3. The timer runs, and meanwhile the other console.log is added to the call stack
Normal tasks
  1. First log and Second log are logged. Once the timer finishes, it moves the callback to the macrotask queue, and the event loop adds that callback to the call stack once the stack is empty.
Web APIs task

Note that the callback queue is really two queues: the microtask and macrotask queues.

The event loop always gives the microtask queue higher priority.

In this example, what order do you think the logs come in?

1console.log('First');
2
3setTimeout(() => {
4  console.log('Second');
5}, 100);
6
7Promise.resolve('Third').then(console.log);
8
9console.log('Fourth');

Applying the same concepts:

  1. console.log('First') and console.log('Fourth') are added to the call stack, logging First and Fourth
  2. The Promise and setTimeout callbacks are already in the task queue. The microtask queue has higher priority than the macrotask queue, so Third is logged next
  3. Finally, the setTimeout callback runs, logging Second

Quick Recap

  1. JavaScript Runtime Model

    • Single-threaded execution
    • Non-blocking operations
    • Asynchronous capabilities
    • Event-driven architecture
  2. Event Loop Components

    • Call Stack: Function execution context
    • Web APIs: Browser-provided capabilities
    • Callback Queue: Task management
    • Microtask & Macrotask queues
  3. Execution Order

    • Synchronous code runs first
    • Microtasks have higher priority
    • Macrotasks run after microtasks
    • Event loop maintains order

References

Related Articles

GET IN TOUCH

Let's work together

I build fast, accessible, and delightful digital experiences for the web. Whether you have a project in mind or just want to connect, I'd love to hear from you.

Get in touch

or reach out directly at hello@mohammadshehadeh.com