Background Fetch

A fetch() dies with its page. Background Fetch instead hands a list of requests to the browser, which downloads them behind a user-visible progress notification that survives a closed tab or a browser restart, then wakes the service worker with backgroundfetchsuccess so the responses can be cached. That suits a podcast episode or a level pack, where Background Sync (SW Background Sync) only retries small deferred posts. abort() cancels a running fetch.

A download that outlives the tabJavaScript
const reg = await navigator.serviceWorker.ready;                      // page
const bg = await reg.backgroundFetch.fetch('ep-5', ['ep-5.mp3', 'ep-5-art.png'],
  { title: 'Episode 5', downloadTotal: 3000 });    // shown in the browser download UI
bg.onprogress = () => console.log(`${bg.downloaded}/${bg.downloadTotal} ${bg.result || '-'}`);
self.addEventListener('backgroundfetchsuccess', (e) => e.waitUntil((async () => {   // sw.js
  const cache = await caches.open('episodes');     // the only chance to keep the bytes
  for (const r of await e.registration.matchAll())
    await cache.put(r.request, await r.responseReady);
})()));
Output
2000/3000 -
3000/3000 -
3000/3000 success

Both files reached the episodes cache in headless Chrome 152 1 over http://localhost. Support is Limited — Chrome 74 and Edge 79 upwards, nothing in Firefox 555 or Safari 10 — so guard on 'backgroundFetch' in reg and fall back to a plain fetch().