SW Background Sync

Service Workers: Background Sync and PeriodicSyncManager

Background Sync finishes queued work, such as an offline message, once the connection returns, even after the tab closes: registration.sync.register(tag) fires a sync event, retried later if its waitUntil() promise rejects. Periodic Background Sync fires periodicsync no more often than minInterval. Both are Limited availability (Chrome 1 and Edge only), and Chrome grants periodic sync only to installed web apps.

Registering sync (page) and handling it (sw.js)JavaScript
const reg = await navigator.serviceWorker.ready;                     // page
await reg.sync?.register('send-outbox');          // queued data waits in IndexedDB
await reg.periodicSync?.register('refresh-news', { minInterval: 12 * 3600 * 1000 })
  .catch(() => {});                               // rejects unless the app is installed
self.addEventListener('sync', (e) => {                               // sw.js
  if (e.tag === 'send-outbox') e.waitUntil(sendQueuedMessages());   // your upload function
});

In headless Chrome the sync event fired at once (it was online) and periodic registration rejected with NotAllowedError. Safari 10 and Firefox 555 lack both, so also flush the queue on load and on online.