JavaScript
04 / 07

Event Loop & Concurrency

JavaScript Event Loop & Concurrency

JavaScript is single-threaded but can handle concurrent operations through the event loop, which manages the execution of code, events, and callbacks.

Call Stack, Web APIs, and Task Queue

console.log('Start');

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

Promise.resolve()
  .then(() => console.log('Promise 1'))
  .then(() => console.log('Promise 2'));

console.log('End');

// Output:
// Start
// End
// Promise 1
// Promise 2
// Timeout

// Why? Execution order:
// 1. Synchronous code runs first (Call Stack)
// 2. Microtasks (Promises) run next (Microtask Queue)
// 3. Macrotasks (setTimeout, setInterval) run last (Task Queue)

Microtasks vs Macrotasks

// Microtasks (higher priority):
// - Promise callbacks (.then, .catch, .finally)
// - queueMicrotask()
// - MutationObserver

// Macrotasks (lower priority):
// - setTimeout, setInterval
// - setImmediate (Node.js)
// - I/O operations
// - UI rendering

console.log('1');

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

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

queueMicrotask(() => console.log('4 - queueMicrotask'));

console.log('5');

// Output:
// 1
// 5
// 3 - Promise
// 4 - queueMicrotask
// 2 - setTimeout

Web Workers (True Concurrency)

// Main thread
const worker = new Worker('worker.js');

// Send message to worker
worker.postMessage({ type: 'CALCULATE', numbers: [1, 2, 3, 4, 5] });

// Receive message from worker
worker.onmessage = (event) => {
  console.log('Result from worker:', event.data);
};

worker.onerror = (error) => {
  console.error('Worker error:', error);
};

// worker.js
self.onmessage = (event) => {
  const { type, numbers } = event.data;
  
  if (type === 'CALCULATE') {
    // Heavy computation in separate thread
    const result = numbers.reduce((sum, n) => sum + n, 0);
    self.postMessage(result);
  }
};

Keep your own version of these notes — editable, searchable, and organised by your stack.

Start free