Articles

Web Workers vs Service Workers: What's the Difference

Web workers run scripts off the main thread for parallel computation. Service workers intercept network requests for offline and caching. How they differ.

Takina Takina · · 4 min read
Abstract illustration of parallel browser processes

Web workers and service workers are both background scripts that run outside the browser’s main thread, but they solve different problems. A web worker exists to run CPU-heavy JavaScript in parallel so it doesn’t block the UI. A service worker exists to sit between your app and the network, intercepting requests to enable caching and offline behavior. The name similarity causes constant confusion, but their APIs, lifecycles, and use cases barely overlap.

Web workers: parallel computation

The browser’s JavaScript engine is single-threaded — one thread handles user input, layout, painting, and your application code. A long-running computation on that thread (parsing a large JSON payload, running an image filter, computing a hash) blocks everything else, including scrolling and clicks. Understanding why requires knowing how the event loop works: there’s no preemption, so a slow synchronous function just runs to completion while the page freezes.

A web worker spins up a genuinely separate thread with its own global scope. It has no access to the DOM, window, or your page’s variables — communication happens exclusively through postMessage() and message events, passing structured-cloned data back and forth (or transferring ownership of large buffers with Transferable objects, avoiding a copy).

// main.js
const worker = new Worker("worker.js");
worker.postMessage({ data: largeArray });
worker.onmessage = (e) => console.log("Result:", e.data);

// worker.js
self.onmessage = (e) => {
  const result = expensiveComputation(e.data.data);
  self.postMessage(result);
};

Web workers are created per-tab and terminate when the tab closes or you call worker.terminate(). They’re the right tool for image or video processing, parsing large files, cryptographic operations, or any computation heavy enough to visibly stutter the UI.

Service workers: network proxy and offline

A service worker is a script the browser registers once and keeps running independently of any single page — it can outlive the tab that registered it and intercepts every fetch request the page makes, deciding whether to serve from cache, hit the network, or do both.

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

This makes service workers the foundation of offline-capable sites and PWAs: cache the app shell on install, serve it instantly on repeat visits, and fall back to cached responses when the network is unavailable. They also enable background sync and push notifications — capabilities that require the browser to wake the worker even when no tab is open.

Service workers have a distinct lifecycle (install → activate → fetch/idle) with versioning rules designed to avoid serving a broken cache: a new service worker installs alongside the old one and only takes over once existing tabs close or explicitly call skipWaiting(). That lifecycle is one of the more error-prone parts of the web platform — stale caches from a botched service worker update are a common source of “why won’t my site update” bug reports.

Side-by-side comparison

Web workerService worker
PurposeOff-main-thread computationNetwork interception, caching, offline
LifetimeTied to the page that created itPersists across page loads, independent of any tab
Network accessCan fetch, but doesn’t intercept the page’s requestsIntercepts and controls every fetch from controlled pages
DOM accessNoneNone
CommunicationpostMessage with the page that spawned itControls fetch events; talks to pages via postMessage or the Clients API
Typical useParsing, image processing, crypto, heavy computationOffline caching, PWA install, push notifications, background sync
Number per pageOne or more, created and destroyed freelyOne controller per scope, shared across all matching tabs

When to use which — or both

They’re not mutually exclusive. A PWA might use a service worker to cache static assets and serve an offline fallback page, while also spinning up a web worker to decode a large image or run a search index off the main thread. Neither one substitutes for the other: a service worker doing heavy synchronous computation inside a fetch handler will delay every intercepted request, and a web worker has no ability to intercept network calls at all.

If your problem is “the UI freezes during this computation,” you want a web worker. If your problem is “this needs to work offline” or “repeat loads should be instant,” you want a service worker. Both run outside the main thread, which is the source of the naming overlap — but that’s essentially where the similarity ends. For performance work more broadly, see the Core Web Vitals guide for how blocking the main thread affects real user metrics.

The takeaway

Web workers parallelize CPU-bound JavaScript; service workers intercept network requests for caching and offline support. Neither touches the DOM, and neither is a drop-in replacement for the other — a heavy computation belongs in a web worker, and offline or cache-control logic belongs in a service worker. Reach for one, the other, or both depending on which problem you’re actually solving.

Takina Takina · · 4 min read

requestIdleCallback Explained

requestIdleCallback runs low-priority JavaScript when the browser is idle, without blocking rendering, input, or the main thread.

#JavaScript #Performance #Web Development
Takina Takina · · 5 min read

Finding and Fixing Memory Leaks in JavaScript

A JavaScript memory leak happens when a reference outlives its usefulness and the garbage collector can't reclaim it. Common causes and how to find them.

#JavaScript #Web Development #Performance