Web Workers and Service Workers: When and How

Web Workers and Service Workers are two confusingly-named browser APIs with very different purposes. Senior frontend interviews probe whether you understand which is which and when to use each. Most engineers know one but not the other.

Web Workers

Run JavaScript on a separate thread. Communicates with main thread via postMessage.

Use cases:

  • Heavy computation (image processing, ML inference)
  • Parsing large data (CSV, JSON, Excel)
  • Cryptography
  • Compression / decompression

Benefit: keep the main thread free; UI stays responsive.

Web Worker example

// main.js
const worker = new Worker('worker.js');
worker.postMessage({ type: 'process', data });
worker.onmessage = (e) => { console.log('result', e.data); };

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

Web Worker constraints

  • No DOM access
  • No window or document
  • Can use fetch, IndexedDB, WebSockets
  • Communication only via postMessage (structured clone)
  • One module per worker

Service Workers

Different beast. Service Workers intercept network requests and act as a proxy between the page and the network.

Use cases:

  • Offline support (cache responses; serve from cache when offline)
  • Push notifications
  • Background sync (queue actions while offline; sync when online)
  • Asset caching for performance (cache-first strategies)

Service Worker example

// register
navigator.serviceWorker.register('/sw.js');

// sw.js
self.addEventListener('fetch', (e) => {
  e.respondWith(
    caches.match(e.request).then(r => r || fetch(e.request))
  );
});

Service Worker lifecycle

  1. Install: triggered first time the SW registers
  2. Activate: when the SW takes control
  3. Idle: waiting for events
  4. Update: a new SW found; install in background; activate when old clients close

The lifecycle is famously tricky — debugging SW updates is painful.

Caching strategies

  • Cache-first: serve from cache, fall back to network. Fast, can be stale.
  • Network-first: try network, fall back to cache. Fresh, slower offline.
  • Stale-while-revalidate: serve cache; update cache in background. Fast + eventually fresh.
  • Network-only: bypass cache for sensitive data.
  • Cache-only: serve only from cache (offline-first).

Workbox library helps implement these patterns reliably.

PWA basics

Service Worker + Web App Manifest = installable PWA:

  • Service Worker for offline behavior
  • Manifest declares icons, name, theme color
  • Browser shows “install” prompt on engaged users

Background sync

Queue actions while offline. When online, sync.

self.addEventListener('sync', (e) => {
  if (e.tag === 'sync-tasks') {
    e.waitUntil(syncQueue());
  }
});

Use case: user marks task complete while offline; SW queues it; on next online, syncs to server.

Common gotchas

Service Worker

  • Cached SW does not update; users see stale code for hours
  • Browser DevTools shows “Update on reload” — turn it on during development
  • HTTPS required (except localhost)
  • Scope is path-based; cannot intercept requests outside its scope

Web Worker

  • Communication is async and overhead has cost
  • Workers are heavy to start; do not spawn one per task
  • Module workers (Chrome 80+) support import/export properly

Shared Workers

Less commonly used. Multiple tabs share one worker. Useful for cross-tab coordination but not widely supported.

When NOT to use workers

  • For tasks that finish in milliseconds (worker setup overhead exceeds savings)
  • For DOM manipulation (workers cannot)
  • For “everything should be in workers” — unnecessary complexity

Frequently Asked Questions

Should I always use a Service Worker for offline support?

For PWAs and content sites: yes. For typical SaaS: optional — depends on offline UX expectations.

How do I debug Service Workers?

Chrome DevTools → Application → Service Workers. Toggle “Update on reload” during development. Use the unregister button when state gets confused.

Can I run an LLM in a Web Worker?

Yes — small models (~100MB) can run via WebAssembly in a Worker. Larger models exceed memory limits. Tools like web-llm and transformers.js make this practical.

Scroll to Top