demo · v139
Freeze Budget Explainer
Side-by-side timelines: the same backgrounded tab on Chrome 138 (5-min budget) and Chrome 139 (1-min budget on Android). Pick a workload — a polling timer, an open WebSocket, a Service Worker fetch — and see exactly when it gets killed.
10s
Chrome 138 (Android) — 5 min budget
active
throttled
frozen
Chrome 139 (Android) — 1 min budget
active
throttled
frozen
what to do
- Move long timers to a Service Worker. SW
setTimeoutis governed by the same budget, but Service Workers are designed for periodic wake (push, periodic sync). - Persist state proactively. Treat any backgrounded event as “might be the last thing you ever run”. Use
visibilitychange+pagehideas a save point. - Use Background Sync for “run when next online” tasks instead of polling.
- Use
fetch(url, { keepalive: true })for departure beacons — they survive freeze. Pair with the new keepalive retry in v139.
snippet
// Save before the tab might freeze.
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
fetch('/save', {
method: 'POST',
body: JSON.stringify(draftState()),
keepalive: true, // outlives the freeze
// Chrome 139 will retry this beacon if the network drops:
retryOptions: { maxAttempts: 3, initialDelay: 1000 },
});
}
});
// Replace setInterval polling with periodic sync.
const reg = await navigator.serviceWorker.ready;
await reg.periodicSync.register('refresh-feed', {
minInterval: 24 * 60 * 60 * 1000, // 24h
});