Frontend Labs
⋯

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.

Generated from real-life-JS-guide.md · 8 Oct 2026 16:36