JavaScriptMarch 22, 2026

JavaScript Event Loop Explained

A deep dive into the JavaScript event loop and how it manages async code.

JavaScript Event Loop Explained

Understanding the event loop is crucial for writing efficient JavaScript. Let’s break it down.

The Call Stack

JavaScript is single-threaded, with one call stack:

javascript
function third() { console.log('Third') }
function second() { third(); console.log('Second') }
function first() { second(); console.log('First') }

first()
// Third, Second, First

Web APIs and Task Queue

javascript
console.log('Start')

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

console.log('End')

// Output: Start, End, Timeout

Microtasks vs Macrotasks

javascript
console.log('1')

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

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

console.log('4')

// Output: 1, 4, 3, 2

Microtasks (higher priority):

  • Promise callbacks
  • queueMicrotask()
  • MutationObserver

Macrotasks:

  • setTimeout/setInterval
  • I/O operations
  • UI rendering

Event Loop Cycle

  1. Execute all synchronous code
  2. Execute all microtasks
  3. Execute one macrotask
  4. Repeat

Practical Example

javascript
async function async1() {
  console.log('async1 start')
  await async2()
  console.log('async1 end')
}

async function async2() {
  console.log('async2')
}

console.log('script start')

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

async1()

new Promise((resolve) => {
  console.log('promise1')
  resolve()
}).then(() => console.log('promise2'))

console.log('script end')

// Output:
// script start
// async1 start
// async2
// promise1
// script end
// async1 end
// promise2
// setTimeout