Skip to main content

Web Workers in JavaScript

A Web Worker is a separate JavaScript execution thread that runs in parallel with the main thread (the UI thread). It does not block the interface: heavy computation runs in the background, and the result comes back to the main thread as a message.

Theory

TL;DR

  • JavaScript in the browser is single threaded: rendering, event handling and your code all live on one thread.
  • A heavy synchronous loop occupies the Event Loop, and the page freezes.
  • A Web Worker moves the computation onto a separate thread, so the UI stays responsive.
  • The threads talk only through messages: postMessage and onmessage, with data serialised by the structured clone algorithm.
  • A worker has no access to document, window, alert() or localStorage, but it does have fetch, XMLHttpRequest, WebSocket, setTimeout, indexedDB and crypto.
  • There are three types: Dedicated Worker, Shared Worker, Service Worker.

Quick example

javascript
// main.js const worker = new Worker('worker.js'); worker.postMessage(1e9); // send the task worker.onmessage = (event) => { console.log('Result:', event.data); };
javascript
// worker.js onmessage = (event) => { let sum = 0; for (let i = 0; i < event.data; i++) sum += i; postMessage(sum); // send the result back };

Now the heavy calculation runs in the background while the UI stays responsive.

The single threaded JavaScript problem

JavaScript in the browser is single threaded, so one thread does everything at once:

  • rendering the page
  • handling events
  • running your code

If you start a heavy loop on that thread:

javascript
for (let i = 0; i < 1e9; i++) {} console.log('Done!');

the page freezes: the interface stops responding until the loop finishes, because the Event Loop is busy.

How it works under the hood

  1. The main thread creates a worker with new Worker('script.js').
  2. The browser starts a separate JS thread.
  3. The threads communicate through messages (message passing).
  4. The transfer is asynchronous and the data is serialised (structured clone).
  5. The worker has no access to the DOM, window or document.

Message passing

Communication goes through the postMessage and onmessage events:

javascript
// main.js worker.postMessage({ action: 'sum', a: 5, b: 7 }); // worker.js onmessage = (e) => { if (e.data.action === 'sum') { postMessage(e.data.a + e.data.b); } };

You can send:

  • strings, numbers, objects
  • ArrayBuffer, Blob, File, ImageBitmap
  • even Transferable objects, to hand over data without copying it

Limits and the available API

A Web Worker is isolated:

  • no access to document, window, alert(), localStorage
  • access to fetch, XMLHttpRequest, WebSocket, setTimeout, indexedDB, crypto and the like

Stream processing inside a worker

A worker can use Streams and the Fetch API:

javascript
onmessage = async (e) => { const response = await fetch(e.data.url); const text = await response.text(); postMessage(text.length); };

That way network data is processed in the background without blocking the UI at all.

Types of Web Workers

TypeWhere it is usedCharacteristics
Dedicated WorkerOne to oneWorks only with the script that created it
Shared WorkerSeveral tabs or scriptsOne worker for all pages (per origin + URL)
Service WorkerBetween the browser and the networkCaches requests, works even offline

Shared Worker example

javascript
// shared-worker.js onconnect = (event) => { const port = event.ports[0]; port.onmessage = (e) => { port.postMessage(`Received: ${e.data}`); }; };

Several tabs can connect to a single Shared Worker, for example to exchange data between tabs.

Service Worker as a special type

A Service Worker is not just a thread, it is a proxy between the browser and the network.

It intercepts HTTP requests, caches responses and makes offline work possible. Examples: PWA applications, push notifications, background sync.

javascript
// service-worker.js self.addEventListener('fetch', (event) => { event.respondWith( caches.match(event.request).then((cached) => { return cached || fetch(event.request); }) ); });

This means: if a cached response exists, serve it, otherwise load it from the network.

An example of "parallel" computation

javascript
const workers = Array.from({ length: 4 }, () => new Worker('calc.js')); let completed = 0; let result = 0; workers.forEach((w, i) => { w.postMessage(i); w.onmessage = (e) => { result += e.data; if (++completed === 4) console.log('Done:', result); }; });

This spreads the computation across several threads, a kind of pseudo parallelism.

When Web Workers are worth it

ScenarioWhy it helps
Complex computationJSON parsing, cryptography, machine learning
Large data processingfiltering and aggregating arrays, handling CSV
Image workcompression, filters, thumbnail generation
Games and animationphysics calculations without interface lag
WebSocket plus data analysisstream processing

A simple analogy:

Imagine the browser is a kitchen. The main thread (the main JS) is the chef who cooks the dish and serves the guests. The Web Worker is an assistant in another kitchen who chops the vegetables. The chef hands over a task, the assistant does it in the background and returns the result. Neither one gets in the other's way.

Summary:

ComponentWhat it does
Web WorkerA separate thread for running code
Main advantageDoes not block the UI
Data exchangeThrough postMessage / onmessage
LimitsNo access to the DOM, window, document
TypesDedicated, Shared, Service Worker
Main usesBackground computation, caching, offline mode, sync

Common mistakes

  • Reaching for document or window inside a worker. They are not there, so the data you need has to arrive as a message.
  • Expecting an object to be passed by reference. Data is copied by structured clone, so large arrays are better handed over as Transferable objects (ArrayBuffer), otherwise the copying itself becomes the bottleneck.
  • Sending functions, DOM nodes or class instances with methods through postMessage. Structured clone does not support them and throws.
  • Spawning a new worker for every small call. Starting a thread costs time and memory, so reuse workers or keep a pool of them.
  • Confusing a Web Worker with a Service Worker. The first is for computation, the second for intercepting network requests and offline mode.
  • Forgetting worker.terminate(). A worker stays alive until you stop it explicitly or the page is closed.

Short Answer

Interview ready
Premium

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