← Writing

What "Non-Blocking JavaScript" Actually Means

JavaScript can start asynchronous work without making the single main thread magically perform two tasks at once.

On this pageReady is not the same as running

Calling JavaScript “non-blocking” is useful shorthand, but it can suggest something that the browser does not promise. JavaScript still runs one main-thread task at a time. An endless loop blocks a tab just as thoroughly today as it did years ago.

What changes with an asynchronous API is ownership of the wait. fetch() asks the browser to begin a network operation, then returns a promise. JavaScript does not sit inside the function until bytes arrive. The browser handles the network work elsewhere and schedules the promise continuation when a result is ready.

const response = await fetch('/api/weather');
const forecast = await response.json();

renderForecast(forecast);

The await keyword pauses this async function. It does not pause the whole thread. Other tasks can run before renderForecast() becomes eligible to continue.

Ready is not the same as running#

A completed request cannot interrupt JavaScript that is already executing. Its continuation joins a queue. If the main thread is busy, the callback waits even though the network finished several milliseconds ago.

That distinction explains a common debugging surprise. The server responds quickly, but the UI updates late. Network timing is only one part of the path. Main-thread availability decides when JavaScript can process the result.

“Non-blocking” therefore describes how an API begins work and yields control. It is not a general property of every line of JavaScript. CPU-heavy work still needs smaller tasks, a worker, or a different design.