principlejavascriptMajor
Service worker lifecycle — install, activate, and fetch explained
Viewed 0 times
Service Worker API (Chrome 40+, Firefox 44+, Safari 11.1+)
service worker lifecycleinstall activate fetchskipWaitingclients.claimPWA cachingsw.js
browser
Problem
Developers register a service worker and expect it to work immediately, but the new worker enters a waiting state while the old one controls existing pages. Updates seem to not apply, confusing developers and users.
Solution
Understand the three lifecycle phases and how to handle updates.
// Registration
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js').then(reg => {
console.log('SW registered:', reg.scope);
});
}
// sw.js — install phase: cache static assets
self.addEventListener('install', event => {
event.waitUntil(
caches.open('v1').then(cache =>
cache.addAll(['/index.html', '/app.js', '/app.css'])
)
);
// Force new SW to activate immediately (skip waiting)
self.skipWaiting();
});
// activate phase: clean old caches
self.addEventListener('activate', event => {
event.waitUntil(
caches.keys().then(keys =>
Promise.all(keys.filter(k => k !== 'v1').map(k => caches.delete(k)))
)
);
// Take control of all pages immediately
self.clients.claim();
});
// fetch phase: intercept network requests
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request).then(resp => resp || fetch(event.request))
);
});
// Registration
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js').then(reg => {
console.log('SW registered:', reg.scope);
});
}
// sw.js — install phase: cache static assets
self.addEventListener('install', event => {
event.waitUntil(
caches.open('v1').then(cache =>
cache.addAll(['/index.html', '/app.js', '/app.css'])
)
);
// Force new SW to activate immediately (skip waiting)
self.skipWaiting();
});
// activate phase: clean old caches
self.addEventListener('activate', event => {
event.waitUntil(
caches.keys().then(keys =>
Promise.all(keys.filter(k => k !== 'v1').map(k => caches.delete(k)))
)
);
// Take control of all pages immediately
self.clients.claim();
});
// fetch phase: intercept network requests
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request).then(resp => resp || fetch(event.request))
);
});
Why
The waiting state exists to prevent a new service worker from controlling pages that were opened with the old version's cached assets — a mismatch that could cause errors. skipWaiting + clients.claim bypasses this safely in most development scenarios.
Gotchas
- skipWaiting() activates the new SW immediately but does not reload open tabs — add a message to prompt the user to refresh
- Service workers only work on HTTPS (and localhost for development)
- A change in any byte of sw.js triggers the install phase — version your cache names to avoid stale asset conflicts
- event.waitUntil() prevents the browser from terminating the SW before async work completes — always use it
Context
Adding offline support or background caching to any web application
Revisions (0)
No revisions yet.