ข้ามไปยังเนื้อหา

Background Sync

กลยุทธ์ offline ส่วนใหญ่มุ่งเน้นที่การอ่าน content ที่ cache ไว้ แต่การ เขียน ล่ะ — การส่งฟอร์ม, ข้อความแชท, การซื้อ? ถ้าผู้ใช้ส่งข้อมูลขณะ offline การ implement แบบง่าย ๆ จะแสดง error หรือ drop request เงียบ ๆ Background Sync API แก้ปัญหานี้โดยให้คุณ queue operation และให้ service worker replay อัตโนมัติเมื่อการเชื่อมต่อกลับมา แม้ผู้ใช้ปิด tab ไปแล้ว

ขั้นตอนมีสามฝ่าย: หน้าเว็บ, service worker, และ โครงสร้างพื้นฐาน sync ของ browser

  1. หน้าเว็บตรวจพบการเขียนที่ล้มเหลว (หรืออาจ offline)
  2. หน้าเว็บเรียก registration.sync.register('tag-name') เพื่อตั้งเวลา sync event
  3. browser fire sync event บน service worker เมื่อมีการเชื่อมต่อที่เสถียร SW retry operation และเรียก event.waitUntil() เพื่อให้ browser ทำงานต่อจนงานเสร็จ
  4. ถ้าการ sync ล้มเหลว browser จะ retry ด้วย exponential back-off

Background Sync ปัจจุบันรองรับเฉพาะ browser ที่ใช้ Chromium (Chrome, Edge, Samsung Internet, และ browser Android ส่วนใหญ่) Firefox และ Safari ไม่ได้ implement ทำให้ graceful fallback เป็นสิ่งจำเป็น — ถ้า API ไม่พร้อมใช้งาน ควรลอง network request ทันที

async function registerSync(registration) {
if ('SyncManager' in window && registration.sync) {
await registration.sync.register('send-form');
console.log('Sync registered — will retry when online');
} else {
// Browser does not support Background Sync; attempt immediately
console.log('Background Sync not supported — sending now');
await sendQueuedData();
}
}

ตรวจสอบทั้ง 'SyncManager' in window และ registration.sync — Chromium เวอร์ชันเก่าบางรุ่น expose SyncManager แต่ไม่ expose registration.sync

เมื่อผู้ใช้ submit ฟอร์ม ให้บันทึกข้อมูลลง IndexedDB ก่อน จากนั้น register sync tag การเขียนลง IndexedDB คือ record ที่คงทน; sync tag เป็นเพียง signal ให้ service worker

async function handleSubmit(formData) {
// 1. Save to IndexedDB so the SW can read it later
await saveToIdb('outbox', { id: Date.now(), ...formData });
// 2. Get the SW registration
const registration = await navigator.serviceWorker.ready;
if (registration.sync) {
// 3. Register the background sync tag
await registration.sync.register('send-form');
} else {
// Fallback: try the request right now
await sendQueuedData();
}
}

Service worker ฟัง sync event property event.tag ตรงกับ string ที่ส่งให้ registration.sync.register()

self.addEventListener('sync', (event) => {
if (event.tag === 'send-form') {
event.waitUntil(replayOutbox());
}
});
async function replayOutbox() {
const db = await openDatabase();
const tx = db.transaction('outbox', 'readwrite');
const store = tx.objectStore('outbox');
const allRequests = await getAllRecords(store);
for (const item of allRequests) {
const response = await fetch('/api/submit', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(item),
});
if (response.ok) {
// Remove from outbox after successful send
await deleteRecord(store, item.id);
}
}
}

ถ้า replayOutbox() throw หรือคืนค่า rejected Promise browser จะถือว่า sync ล้มเหลวและ schedule retry resolve สำเร็จเฉพาะเมื่อคุณแน่ใจว่าข้อมูลถูกส่งแล้ว

sequenceDiagram
  participant U as User
  participant P as Page
  participant IDB as IndexedDB
  participant SW as Service Worker
  participant API as Server API

  U->>P: Submits form (offline)
  P->>IDB: saveToIdb('outbox', data)
  P->>SW: registration.sync.register('send-form')
  Note over U,SW: Connection restored
  SW->>SW: sync event fires (tag: send-form)
  SW->>IDB: read all outbox records
  IDB-->>SW: queued items
  SW->>API: POST /api/submit
  API-->>SW: 200 OK
  SW->>IDB: delete sent record
Background Sync: queue while offline, replay when connected

Playground ด้านล่างแสดง flow ทั้งหมด หน้าเว็บให้คุณ submit ข้อความ Service worker จะ queue ลงใน in-memory outbox และ register sync tag เมื่อ browser fire sync event SW จะ log การ replay Toggle แผงเครือข่ายของ browser เป็น “Offline” ก่อน submit เพื่อดู queue ทำงานจริง

Runs a real service worker + manifest in your browser.
ตัวเลือกBenefitCost
ใช้ Background Sync APIretry request ที่ล้มเหลวอัตโนมัติแม้ปิด tab ไปแล้วรองรับเฉพาะบาง browser (ไม่มีใน Safari) ต้องมี fallback เอง
Retry ด้วยมือตอนกลับมา online (foreground เท่านั้น)ทำงานได้ทุก browser ไม่ต้องพึ่ง API พิเศษพลาด sync ถ้าผู้ใช้ปิด tab ไปก่อนที่ network จะกลับมา
  • เก็บ mutation ไว้ใน memory แทน IndexedDB ทำให้ข้อมูลหายทันทีที่ service worker ถูก terminate
  • ไม่ทำ retry backoff ทำให้ sync tag ยิง request รัวๆ ซ้ำเมื่อ network กลับมาแต่ server ยัง unstable
  • ไม่ handle กรณี sync ล้มเหลวซ้ำ (permanent failure) ทำให้ queue ค้างพยายาม sync ข้อมูลที่ไม่มีทาง sync สำเร็จตลอดไป

💡 ตัวอย่างจากของจริง

Google Maps — คิว location update และ report ไว้ผ่าน Background Sync แล้วส่งอัตโนมัติทันทีที่กลับมามีสัญญาณ

Notion offline — เก็บการแก้ไขที่ทำตอน offline ไว้ในคิว แล้ว sync กลับ server อัตโนมัติด้วยกลไกคล้าย background sync เมื่อกลับมา online

หน้าเว็บเรียก method ใดเพื่อตั้งเวลา background sync?
ปัจจุบัน Background Sync API รองรับใน browser ใดบ้าง?
ถ้าการ sync ล้มเหลว (network request throw) service worker ควรทำอย่างไร?
ทำไมต้องบันทึกข้อมูลลง IndexedDB ก่อน register sync tag?