Skip to main content

Non-blocking code

Non-blocking code is code that does not stop the thread from executing; it delegates long operations (I/O, network, timers, files) and carries on with other tasks. Its opposite, blocking code, does not release the main thread until it finishes.

Theory

TL;DR

  • Blocking code holds the main thread: while it runs there is no click handling, no repainting and no next function.
  • Non-blocking code hands the long operation outside and moves on immediately, receiving the result later through a callback or a promise.
  • This matters because JavaScript is single-threaded: if one piece of code hangs, the rest waits.
  • The machinery: asynchronous APIs (setTimeout, fetch, async/await), external Web APIs or the libuv thread pool, plus the event loop that returns the result.
  • Typical non-blocking operations: timers, network, files in Node.js, database queries, event listeners, promises.
  • Non-blocking is not the same as parallel: the engine runs one callback at a time, it just does not idle while waiting.

Quick example

javascript
console.log('start'); setTimeout(() => { console.log('one second passed'); }, 1000); console.log('end'); // start // end // one second passed

The thread was never blocked: JS freely handled other events while the timer «worked in the background».

What blocking code is

Blocking code does not release the main thread until it finishes. While the operation runs, JavaScript cannot do anything else.

javascript
// Simulating a long calculation function heavyCalculation() { const start = Date.now(); while (Date.now() - start < 3000) {} // wait 3 seconds console.log('calculation finished'); } console.log('start'); heavyCalculation(); console.log('end');

Result:

javascript
start (frozen for 3 seconds) calculation finished end

Everything stopped for 3 seconds: the interface «froze», the server stopped answering. That is blocking code.

What non-blocking code is

The non-blocking version of the same delay:

javascript
console.log('start'); setTimeout(() => { console.log('one second passed'); }, 1000); console.log('end');

What happens:

  1. JS calls setTimeout and hands the task to the browser (Web API).
  2. Execution continues, 'end' is printed right away.
  3. After 1 second the callback returns to the task queue and runs.

Result:

javascript
start end one second passed

Why this matters in JavaScript

JavaScript is single-threaded, which means:

  • at any moment only one piece of code is executing;
  • if one piece hangs, the rest wait.

To avoid freezes, JS uses:

  • asynchrony (setTimeout, fetch, async/await);
  • external APIs (Web APIs or the libuv thread pool);
  • the event loop, to return the result once it is ready.

Examples of non-blocking operations:

Operation typeExampleWhat it does
TimerssetTimeout, setIntervalrun later
Networkfetch, XMLHttpRequestwait for the server response in the background
Files (Node.js)fs.readFileread the disk asynchronously
Database queriesdb.querywait for the result without blocking
Event listenersaddEventListener('click')fire on an event
PromisesPromise, async/awaitdefer execution until the operation completes

The difference shown in Node.js

The blocking version:

javascript
const fs = require('fs'); console.log('reading the file...'); const data = fs.readFileSync('large.txt', 'utf8'); // blocks the thread console.log('file read:', data.length);

While the file is being read, the server serves no other requests.

The non-blocking version:

javascript
const fs = require('fs'); console.log('reading the file...'); fs.readFile('large.txt', 'utf8', (err, data) => { console.log('file read:', data.length); }); console.log('other code keeps running...');

Node.js hands the task to the system (I/O) and keeps executing. When the file is ready, the callback comes back through the event loop.

Why «non-blocking» is not «parallel»

This is a common interview mistake. Non-blocking code does not mean everything runs in parallel. JS still runs one callback at a time, but it does not waste time waiting for I/O. During that wait the engine handles other tasks, which is what creates the impression of parallelism.

TypeWhat it doesExampleConsequence
BlockingStops the thread until it finishesfs.readFileSync()The UI or the server freezes
Non-blockingDelegates the task, keeps workingfs.readFile(), fetch()High responsiveness

Summary: non-blocking code is a way of writing programs where long operations (I/O, network, timers) run asynchronously, without getting in the way of the main logic and without «freezing» the thread. It underpins JS asynchrony (callbacks, promises, async/await), Node.js performance and smooth interfaces in the browser.

Common mistakes

  • Assuming an async function is automatically non-blocking. A heavy loop inside an async function blocks the thread just like any other.
  • Using the synchronous variants of APIs on the server: fs.readFileSync, crypto.pbkdf2Sync and zlib.gzipSync stall the handling of every request.
  • Confusing non-blocking with parallel and expecting two consecutive await calls to run at the same time. Real concurrency needs Promise.all.
  • Thinking the delay in setTimeout is exact. It is a minimum delay: the callback waits until the stack is empty.
  • Forgetting about CPU-bound work: only Web Workers, Worker Threads or native code can move it off the thread, asynchronous syntax cannot.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.