JavaScriptWeb APIs

What are workers in JavaScript?

The short answer

Workers let you run JavaScript in a separate thread, so heavy computation doesn't freeze the user interface. JavaScript is single-threaded by nature — everything runs on one thread called the main thread. If you block that thread with a long calculation, the page becomes unresponsive. Workers solve this by giving you background threads that can do the heavy lifting while the main thread stays free to handle scrolling, clicks, and animations.

Why workers exist

Let's say you need to process a massive dataset or run a complex algorithm. On the main thread, the user can't click buttons, scroll, or type while that work is happening. The browser might even show a "page unresponsive" dialog. Workers move this work off the main thread entirely.

Web Workers (dedicated workers)

A dedicated worker is the most common type. You create a separate JavaScript file for the worker, spin it up from the main thread, and communicate by sending messages back and forth.

worker.js (runs in a separate thread):

self.addEventListener('message', (event) => {
const limit = event.data;
const primes = [];
for (let i = 2; i < limit; i++) {
let isPrime = true;
for (let j = 2; j <= Math.sqrt(i); j++) {
if (i % j === 0) {
isPrime = false;
break;
}
}
if (isPrime) primes.push(i);
}
self.postMessage(primes);
});

main.js (runs on the main thread):

const worker = new Worker('worker.js');

worker.postMessage(10_000_000);

worker.addEventListener('message', (event) => {
console.log('Found primes:', event.data.length);
});

The main thread sends data to the worker with postMessage, and the worker sends results back the same way. While the worker is crunching numbers, the UI stays completely smooth.

What workers can and can't do

Workers run in a completely separate global context. They don't have access to:

  • The DOM — no document, no querySelector, no manipulating elements
  • The window object — workers have self instead
  • The parent page's variables — workers can't read or modify anything on the main thread directly

Workers do have access to fetch, setTimeout, setInterval, IndexedDB, WebSockets, and importScripts() to load external scripts.

Data passed between the main thread and a worker is copied, not shared. For large data, you can use Transferable objects to transfer ownership without copying — but then the sender can no longer access it:

const buffer = new ArrayBuffer(1024 * 1024);
worker.postMessage(buffer, [buffer]);
// buffer.byteLength is now 0 — ownership was transferred

Shared Workers

A dedicated worker belongs to one page. A SharedWorker can be accessed by multiple pages, tabs, or iframes from the same origin. This is useful when you want a single WebSocket connection or cache shared across browser tabs.

const shared = new SharedWorker('shared-worker.js');
shared.port.start();
shared.port.postMessage('hello from this tab');
shared.port.addEventListener('message', (event) => {
console.log('Received:', event.data);
});

Inside the shared worker, you handle connections through a connect event and communicate with each tab through its port. SharedWorkers are less common in practice because they're trickier to debug, but for coordination across tabs, they're the right tool.

Service Workers

Service Workers are a different beast. They sit between your web application and the network, acting as a programmable proxy. They're the backbone of Progressive Web Apps (PWAs).

Key capabilities:

  • Offline support — intercept network requests and serve cached responses when the user is offline
  • Push notifications — receive messages from a server even when the page isn't open
  • Background sync — queue actions while offline and execute them when connectivity returns
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js');
}

Inside the service worker, you listen for lifecycle and fetch events:

self.addEventListener('install', (event) => {
event.waitUntil(
caches.open('v1').then((cache) => {
return cache.addAll([
'/index.html',
'/styles.css',
'/app.js',
]);
})
);
});

self.addEventListener('fetch', (event) => {
event.respondWith(
caches
.match(event.request)
.then((cached) => cached || fetch(event.request))
);
});

Service Workers have a distinct lifecycle (install, activate, fetch) and persist even after the page is closed. They only work over HTTPS (except on localhost), because intercepting network requests requires a secure context.

When to use which type

Dedicated Web Workers — CPU-intensive work on a single page: image processing, data parsing, complex calculations, encryption.

Shared Workers — multiple tabs or pages need to coordinate: shared WebSocket connections, synchronized state across tabs.

Service Workers — offline support, push notifications, or network request interception. They're not for heavy computation — they're for controlling how your app interacts with the network.

Why interviewers ask this

Workers test whether you understand JavaScript's single-threaded nature and how to work around it. Senior developers need to know when to move work off the main thread to keep applications responsive. The question also gauges your awareness of the browser's architecture — that the DOM is only accessible from the main thread, that communication happens through message passing, and that Service Workers enable capabilities like offline-first experiences.