PWA中联网(开启WiFi)时Background Sync代码无法自动执行问题
Hey there, let's break down why your Background Sync isn't triggering automatically when you go online, and fix it step by step.
First, the Core Problem in Your Code
Looking at your service worker code, the biggest issue is that you're not properly waiting for all your fetch requests to finish before the service worker exits. When you loop through your IndexedDB data and fire off fetch calls, those are asynchronous operations—but your current code doesn't tell event.waitUntil() to wait for all of them to complete. The service worker might terminate before the sync finishes, leaving your data stuck in IndexedDB.
Fix 1: Properly Wait for All Sync Operations
Rewrite your sync handler to collect all fetch promises and wait for them to resolve with Promise.all(). Here's the adjusted code:
self.addEventListener('sync', function(event) { console.log('Background syncing...', event); if (event.tag === 'sync-new-posts') { console.log('Syncing new Posts...'); event.waitUntil( readAllData('sync-posts') .then(function(data) { // Collect all sync promises to wait on const syncPromises = []; for (const dt of data) { const postPromise = fetch('xxx some firebase post url xxx', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, body: JSON.stringify({ id: dt.id, title: dt.title, content: dt.content }) }) .then(function(res) { console.log('Sent data', res); if (res.ok) { // Delete the item using the original dt.id (more reliable than resData.id) return deleteItemFromData('sync-posts', dt.id); } throw new Error('Failed to sync post to Firebase'); }) .catch(function(err) { console.log('Error while sending data', err); throw err; // Ensure the promise fails so we know sync didn't work }); syncPromises.push(postPromise); } // Wait for all posts to sync before finishing return Promise.all(syncPromises); }) .catch(err => console.error('Full sync failed:', err)) ); } });
Fix 2: Double-Check Sync Registration
Make sure you're registering the sync event every time you save data to IndexedDB while offline. If you don't trigger the registration, the browser won't know it needs to sync later. Add this code wherever you save posts to sync-posts in IndexedDB:
// After saving data to IndexedDB navigator.serviceWorker.ready.then(swReg => { return swReg.sync.register('sync-new-posts'); }).catch(err => console.error('Failed to register sync event:', err));
Repeating this registration is safe—browsers will ignore duplicate registrations for the same tag.
Fix 3: Fix Firebase ID Matching
Firebase's default POST endpoint generates a new unique ID for each post, which means resData.id won't match the dt.id you sent. This causes deleteItemFromData to fail, leaving old posts in IndexedDB to be retried forever.
You have two options here:
- Use PUT instead of POST to save the post under your existing
dt.id:fetch(`xxx some firebase post url xxx/${dt.id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, body: JSON.stringify({ title: dt.title, content: dt.content }) }) - Delete using the original
dt.id(like we did in the first fix) since you already know which entry to remove from IndexedDB.
How to Test Properly
To confirm your sync works:
- Save a post while offline.
- Close your tab (or put the app in the background)—Background Sync often triggers when the app isn't active.
- Turn on WiFi.
- Check Chrome DevTools > Application > Background Sync to see if the sync event fires, and watch the console for logs.
内容的提问来源于stack exchange,提问作者Robin Rai

