Phase 4 · Async, Data & ToolingModule 25~44 min read

The Event Loop & Async Model

Build an accurate mental model of the call stack, tasks, microtasks, and non-blocking execution.

What you'll learn

JavaScript executes one call stack at a time, while host APIs schedule future work. The event loop decides when queued tasks and microtasks can run and when rendering gets a turn.

By the end of this lesson, you'll be able to:

  • Trace stack, task, and microtask order
  • Explain why timers are not exact
  • Split work that would block rendering

Core mental model

Use this decision table as a compact reference. Focus on what each tool means and when it earns its place in production code.

ConceptWhat it meansDecision rule
Call stackCurrently executing synchronous framesKeep each uninterrupted turn short
TaskQueued event, timer, or message callbackUse task boundaries to yield to the browser
MicrotaskPromise continuation drained before the next taskDo not create endless microtask chains

Professional workflow

Build the behavior in small, observable steps. Each step should leave something you can inspect or test.

  1. Describe the asynchronous scheduling boundary: inputs, outputs, state, timing, and expected failures.
  2. Implement the smallest correct path with names that expose intent.
  3. Add edge cases and failure handling before introducing abstractions.
  4. Verify behavior with realistic data and one deliberately adversarial example.
  5. Refactor only after the observable behavior is protected.

Make behavior observable

Before optimizing or abstracting, make inputs, outputs, state changes, timing, and failure paths visible. JavaScript becomes much easier to reason about when hidden work is exposed.

Guided code lab

Predict event-loop order

Synchronous code finishes, microtasks drain, then the timer task runs.

event-loop.js
console.log("A");
setTimeout(() => console.log("task"), 0);
Promise.resolve().then(() => console.log("microtask"));
console.log("B");

Yield during large work

Chunking prevents one loop from monopolizing the main thread.

chunk-work.js
async function processInChunks(items, size = 100) {
  for (let start = 0; start < items.length; start += size) {
    const chunk = items.slice(start, start + size);
    chunk.forEach(processItem);
    await new Promise(resolve => setTimeout(resolve, 0));
  }
}

Production practice

Contract

Document the asynchronous scheduling inputs, completion signal, failure channel, ordering, and cancellation behavior.

Verification

Test success, expected failure, timeout or cancellation, empty data, and out-of-order completion.

Operations

Expose duration and failure context without logging secrets or overwhelming the main thread.

Common failure mode

A zero-millisecond timer means 'not before this delay after the stack clears,' not immediate execution.

Independent workshop

Create an event-loop visualizer that logs synchronous, microtask, timer, and user-event steps.

Your finished workshop must include:

  • A predicted order before execution
  • A long-task example
  • A chunked version that keeps input responsive

Definition of done

Demonstrate the happy path and at least two edge cases, keep responsibilities separated, and add a short note explaining one design choice.

Recap & quick check

Key takeaways

  • One JavaScript stack runs at a time
  • Host APIs schedule callbacks
  • Microtasks drain before the next task
  • Long turns delay input and rendering

Quick check

1. What runs first after synchronous code?

2. Does setTimeout(fn, 0) run immediately?

3. What causes a long task?

Keep the workshop. Later modules deliberately build on these decisions, so each exercise can become part of your final portfolio architecture.