JavaScript Event Loop and Scheduling

JavaScript runs your code on a single thread per realm. Everything asynchronous — timers, network responses, promise callbacks, user input, rendering — is coordinated by the event loop, which decides what runs next. Most of the time you can ignore it. Then a spinner never appears because a loop blocks rendering, a promise callback runs before a timeout you expected to run first, a recursive promise chain freezes the page, or a Node.js server stops answering health checks while it parses a large JSON file. Each of these is explained by a few rules about tasks, microtasks, and rendering.

This page explains those rules in browsers and Node.js, with traces you can run, and shows how to keep long work from blocking everything else.

TL;DR

Quick Example

Predict the output, then run it:

The script is one task. When it finishes, the microtask queue drains — both microtasks run before the timer task, even with a delay of 0.

Node.js adds its own queues:

Core Concepts

Tasks and Microtasks

An await splits a function: everything after it runs as a microtask once the awaited promise settles.

The Browser Loop

A simplified iteration: take one task, run it to completion; run all microtasks; if it is time to render (typically each display refresh), run requestAnimationFrame callbacks, recalculate style and layout, and paint. Any code that runs for 200 milliseconds therefore delays input handling and painting by 200 milliseconds — a long task, and a direct cause of poor Interaction to Next Paint.

The Node.js Loop

Node.js uses libuv, which cycles through phases:

Between each callback, Node.js drains the process.nextTick queue first, then the promise microtask queue. Heavy use of nextTick can starve I/O just like recursive promises. See Node.js.

Timers Are Minimums

setTimeout(fn, 10) means "not before 10 ms", not "at 10 ms". Browsers clamp deeply nested timers to at least 4 ms and throttle timers in background tabs heavily, often to once per second or less. A busy loop delays every timer.

Keeping the Loop Responsive

Best Practices

Think in Tasks When Debugging Order

When callbacks run in a surprising order, label each as task, microtask, or animation frame. The ordering almost always follows from those categories.

Measure Long Tasks

Use the Long Animation Frames API, performance panel recordings, and field INP data to find work that blocks the loop, rather than guessing.

Avoid Synchronous APIs on Hot Paths

In Node.js, fs.readFileSync, crypto.pbkdf2Sync, and large JSON.parse calls block every request. See Node.js Diagnostics and Event Loop Blocking.

Do Not Rely on Timer Precision

Use timestamps for elapsed time and treat timer delays as lower bounds, especially in background tabs.

Common Mistakes

Expecting a Spinner During Synchronous Work

Microtask Starvation

Assuming setTimeout(0) Runs Immediately

FAQ

What is the JavaScript event loop?

The mechanism that runs queued work on JavaScript's single thread: it takes a task, runs it to completion, drains the microtask queue, and, in browsers, renders when needed, then repeats.

What is the difference between a task and a microtask?

Tasks (timers, events, I/O callbacks) run one per loop iteration. Microtasks (promise continuations, queueMicrotask) run immediately after the current task or callback finishes, all of them, before the next task or render.

Why does a promise callback run before setTimeout with zero delay?

Because promise callbacks are microtasks, which run as soon as the current task completes, while setTimeout schedules a new task that must wait for the next loop iteration.

What is the difference between process.nextTick and setImmediate?

process.nextTick callbacks run before any other queued work, right after the current operation. setImmediate callbacks run in the check phase of the next event loop iteration, after I/O polling.

How do I stop long JavaScript tasks from freezing the page?

Break the work into chunks and yield between them with scheduler.yield() or setTimeout, or move CPU-intensive work to a Web Worker.

Related Topics

References