40. Shared Workers and Service Workers: lifecycle, scope, background sync
| Instances | Lifetime | Purpose | |
|---|---|---|---|
| Dedicated Worker (§38) | One per creating tab | Dies with the tab | Offload computation from one page |
| Shared Worker | ONE instance shared across all same-origin tabs | Dies when the last connected tab closes | Cross-tab state (a shared cache, a single WebSocket serving multiple tabs) |
| Service Worker | ONE per origin, runs independently of any open tab | Survives tab closure; browser can terminate/restart it anytime between events | Network proxy — offline caching, background sync, push notifications |
A Service Worker is the most architecturally distinct: it sits as a
programmable network proxy between your page and the network, letting
you intercept every fetch and decide whether to serve from cache, network,
or a mix — the foundation of every installable, offline-capable Progressive
Web App.
// sw.js — registered once via navigator.serviceWorker.register("/sw.js")
const CACHE_NAME = "app-v1";
const PRECACHE_URLS = ["/", "/app.js", "/app.css", "/offline.html"];
self.addEventListener("install", (event) => {
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => cache.addAll(PRECACHE_URLS))
);
});
self.addEventListener("fetch", (event) => {
event.respondWith(
caches.match(event.request).then((cached) => {
if (cached) return cached; // cache-first for known assets
return fetch(event.request).catch(() => caches.match("/offline.html")); // offline fallback
})
);
});
self.addEventListener("activate", (event) => {
// clean up old cache versions on activation
event.waitUntil(
caches.keys().then((keys) =>
Promise.all(keys.filter((k) => k !== CACHE_NAME).map((k) => caches.delete(k)))
)
);
});
The Service Worker lifecycle (install → waiting → activate) is
deliberately conservative — a new Service Worker version installs
alongside the currently active one and only takes over once all tabs
using the old version have closed (unless you explicitly call
self.skipWaiting()), preventing a mid-session version mismatch between
your JS and your cached assets.