Scott McDaniel

Senior Frontend Developer & Sometimes Full Stack Developer

LinkedIn

JavaScript Runtime

Understanding JavaScript's single-threaded execution model and how the event loop processes asynchronous tasks.

Event Loop

How the Event Loop Works

Understanding JavaScript's single-threaded execution model

// The Event Loop processes tasks in this order:
// 1. Call Stack (synchronous code)
// 2. Microtask Queue (Promises, queueMicrotask)
// 3. Task Queue (setTimeout, setInterval, I/O)

console.log('1'); // Synchronous - goes to call stack

setTimeout(() => {
  console.log('2'); // Task - goes to task queue
}, 0);

Promise.resolve().then(() => {
  console.log('3'); // Microtask - goes to microtask queue
});

console.log('4'); // Synchronous - goes to call stack

// Output: 1, 4, 3, 2
// Why? Event loop processes microtasks before tasks

Event Loop Visualization

Interactive diagram showing how the event loop works

Event Loop Visualization

Call Stack

Synchronous code executes here

console.log()
function calls
variable assignments

Event Loop

Moves tasks from queues to call stack

Checks if call stack is empty
Processes all microtasks first
Then process tasks in the task queue

Microtask Queue

Promises, queueMicrotask, process.nextTick

Promise.then()
queueMicrotask()
async/await

Task Queue

setTimeout, setInterval, I/O operations

setTimeout()
setInterval()
fetch()

Interactive Example

Step: 0 / 12
Ready to start
Code
1console.log('Start');
2setTimeout( () => console.log( 'Timeout called'), 0);
3Promise.resolve().then( () => console.log( 'Promise resolved'));
4console.log('End');
Event Loop Flow
Call Stack
Process
Microtask Queue
Process
Task Queue
Event Loop
Console Output

Key Concepts

Microtasks (High Priority)
  • • Processed before next render
  • • Include Promise callbacks
  • • queueMicrotask() function
  • • process.nextTick() (Node.js)
Tasks (Lower Priority)
  • • Processed after current execution
  • • Include setTimeout, setInterval
  • • I/O operations, fetch requests
  • • DOM event handlers

Microtasks vs Tasks

Different types of asynchronous tasks and their priorities


// Microtasks (processed before next render)
Promise.resolve().then(() => console.log('Microtask 1'));
queueMicrotask(() => console.log('Microtask 2'));

// Tasks (processed after current execution and microtasks)
setTimeout(() => console.log('Task 1'), 0);
setInterval(() => console.log('Task 2'), 1000);

// Event listeners are also tasks
button.addEventListener('click', () => {
  console.log('Click event (task)');
});

// Process.nextTick (Node.js only - highest priority microtask)
process.nextTick(() => console.log('Next tick'));

Event Loop Example

Practical example showing event loop execution order


function eventLoopExample() {
  console.log('Start');
  
  setTimeout(() => {
    console.log('Timeout 1');
    Promise.resolve().then(() => {
      console.log('Promise in timeout');
    });
  }, 0);
  
  Promise.resolve().then(() => {
    console.log('Promise 1');
    setTimeout(() => {
      console.log('Timeout in promise');
    }, 0);
  });
  
  console.log('End');
}
// Output:
// Start
// End
// Promise 1
// Timeout 1
// Promise in timeout
// Timeout in promise