Stale-While-Revalidate
Stale-while-revalidate
หัวข้อที่มีชื่อว่า “Stale-while-revalidate”Stale-while-revalidate (SWR) คือสิ่งที่ดีที่สุดของทั้งสองโลก: service worker คืน response จาก cache ทันที — ผู้ใช้จึงไม่ต้องรอ network เลย — และในขณะเดียวกันก็เริ่ม fetch จาก network ในเบื้องหลังเพื่ออัปเดต cache สำหรับครั้งถัดไป
ใน request ครั้งแรกสุดสำหรับ resource หนึ่ง ยังไม่มีอะไรใน cache SW จึงรอ response จาก network แคชไว้ แล้วคืนกลับไป ตั้งแต่ request ครั้งที่สองเป็นต้นไป สำเนาใน cache (ซึ่งอาจเก่า) จะถูกคืนกลับทันทีขณะที่สำเนาสด ๆ ถูก fetch อยู่เงียบ ๆ ในเบื้องหลัง ผู้ใช้เห็นเนื้อหาทันทีและจะได้เวอร์ชันล่าสุดในการเข้าชมครั้งถัดไป
รูปแบบนี้เข้ากันได้ดีโดยธรรมชาติกับ resource ที่อัปเดตเป็นครั้งคราวแต่เวลาโหลดที่ทันทีสำคัญกว่าความสดใหม่ระดับเป๊ะทุกพิกเซล: avatar ของผู้ใช้, ฟีดข่าว, config JSON, ไฟล์ CSS, และ web font ล้วนเป็นตัวเลือกที่ดี ไม่เหมาะกับข้อมูลแบบเรียลไทม์ — ราคาหุ้น, สกอร์สด, หรืออะไรก็ตามที่การช้าไปหนึ่งเวอร์ชันยอมรับไม่ได้
SWR ทำงานอย่างไร
หัวข้อที่มีชื่อว่า “SWR ทำงานอย่างไร”- service worker รับ
fetchevent เข้ามา - เปิด cache และตรวจหา response ที่มีอยู่
- เริ่ม fetch จาก network โดยไม่สนใจ ว่า cache จะมี response หรือไม่ (การ revalidate ในเบื้องหลัง)
- หากมี response ใน cache จะถูกคืนกลับไปยังหน้าเพจทันที; หากไม่มี response ใน cache handler จะรอ network แล้วคืนค่านั้นแทน
- เมื่อ response จาก network มาถึง รายการใน cache จะถูกอัปเดตเงียบ ๆ เพื่อให้ request ครั้งถัดไปได้สำเนาที่สดใหม่ที่สุด
const CACHE = 'swr-v1';
self.addEventListener('fetch', (event) => { event.respondWith( caches.open(CACHE).then((cache) => cache.match(event.request).then((cached) => { const networkFetch = fetch(event.request).then((response) => { cache.put(event.request, response.clone()); return response; }); // Serve cache instantly; fall back to network on miss return cached || networkFetch; }) ) );});sequenceDiagram participant P as Page participant SW as Service Worker participant C as Cache Storage participant N as Network P->>SW: fetch(request) SW->>C: cache.match(request) SW->>N: fetch(request) [background] C-->>SW: cached Response (may be stale) SW-->>P: Response (instant) N-->>SW: fresh Response SW->>C: cache.put(request, fresh.clone()) Note over SW,C: Cache updated for next request
Cache-then-network (SWR ฝั่งเพจ)
หัวข้อที่มีชื่อว่า “Cache-then-network (SWR ฝั่งเพจ)”ยังมีรูปแบบนี้ในเวอร์ชันฝั่งเพจอีกด้วย หน้าเพจยิงทั้งการค้นหาใน cache และการ fetch จาก network พร้อมกัน render ผลลัพธ์จาก cache ทันที แล้วจึงอัปเดต UI เมื่อ response จาก network มาถึง — ไม่ต้องใช้ service worker เลย
async function fetchWithCacheThenNetwork(url) { const cache = await caches.open('swr-v1'); const cached = await cache.match(url);
// Start the network request immediately, don't await yet const networkPromise = fetch(url).then((response) => { cache.put(url, response.clone()); return response.json(); });
// Render the stale cached version right away if available if (cached) { const staleData = await cached.json(); renderUI(staleData); // show immediately }
// Update the UI when the fresh data lands const freshData = await networkPromise; renderUI(freshData);}วิธีนี้มีประโยชน์ใน framework ที่ component สามารถ re-render เมื่อข้อมูลสด ๆ มาถึง ให้ประสบการณ์ที่ค่อย ๆ ดีขึ้นโดยไม่มี loading spinner ที่มองเห็นได้เลย
ข้อแลกเปลี่ยน
หัวข้อที่มีชื่อว่า “ข้อแลกเปลี่ยน”| ตัวเลือก | Benefit | Cost |
|---|---|---|
| Stale-while-revalidate | ตอบกลับเร็วจาก cache ทันทีพร้อมอัปเดตเบื้องหลัง ได้ทั้งความเร็วและความสด (รอบถัดไป) | ผู้ใช้เห็นข้อมูลเก่าไปก่อน 1 รอบเสมอ ไม่เหมาะกับข้อมูลที่ต้อง real-time |
| Cache-first ล้วนๆ | ง่ายกว่า ไม่ต้องจัดการ background fetch | ไม่มีกลไก auto-refresh ข้อมูลจะเก่าค้างจนกว่าจะ invalidate cache เอง |
ข้อผิดพลาดที่พบบ่อย
หัวข้อที่มีชื่อว่า “ข้อผิดพลาดที่พบบ่อย”- ไม่ await การ update cache เบื้องหลัง (fire-and-forget ผิดจังหวะ) ทำให้ browser อาจ terminate service worker ก่อน fetch เสร็จ ต้องใช้
event.waitUntil() - ใช้ SWR กับข้อมูลที่ต้องถูกต้อง 100% เสมอ (เช่นยอดเงินคงเหลือ) ทำให้ผู้ใช้เห็นตัวเลขเก่าโดยไม่รู้ตัว
- ไม่ trim cache entry เก่าออก ทำให้ storage โตเรื่อยๆ เพราะทุก revalidate จะเขียนทับแต่ไม่เคย cleanup entry ที่ไม่ใช้แล้ว
💡 ตัวอย่างจากของจริง
HTTP Cache-Control: stale-while-revalidate — เป็น pattern มาตรฐานที่ CDN อย่าง Cloudflare และ Fastly ใช้ก่อนที่ service worker จะยืมแนวคิดนี้มาใช้ฝั่ง client
Workbox StaleWhileRevalidate strategy — ใช้กันแพร่หลายในเว็บข่าวและ e-commerce ที่ต้องการทั้งความเร็วของหน้า listing และข้อมูลที่ไม่เก่าเกินไป