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
- Install: triggered first time the SW registers
- Activate: when the SW takes control
- Idle: waiting for events
- 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.