Synchronous กับ Asynchronous storage
Main thread เป็น execution lane เดียวของ JavaScript
หัวข้อที่มีชื่อว่า “Main thread เป็น execution lane เดียวของ JavaScript”browser รัน JavaScript, layout และ painting บน main thread เดียว เมื่อ JavaScript call ใดใช้เวลานานในการ return จะหน่วงทุกอย่างที่เหลือ animations จะกระตุก clicks จะไม่ตอบสนอง และ browser อาจแสดงคำเตือน “page not responding” ปัญหานี้เรียกว่า jank
Synchronous storage: Web Storage และ cookies
หัวข้อที่มีชื่อว่า “Synchronous storage: Web Storage และ cookies”localStorage, sessionStorage และ document.cookie ล้วนเป็น synchronous เมื่อคุณเรียก localStorage.getItem('key') browser จะอ่านจาก disk คืนค่า และโค้ดของคุณจึงจะดำเนินต่อ script ของคุณไม่สามารถทำอะไรอื่นระหว่างรอ
สำหรับการอ่านเล็กน้อยครั้งเดียว ผลกระทบนี้แทบมองไม่เห็น แต่ปัญหาจะเกิดขึ้นเมื่อ:
- วนลูปผ่าน keys จำนวนหลายพัน
- เก็บ serialised objects ขนาดใหญ่ (หลาย kilobytes ต่อรายการ)
- เขียนทุก keystroke หรือ scroll event
- parse cookie string ทั้งหมดบน hot code path
เนื่องจากทั้งหมดนี้เกิดขึ้นบน main thread การใช้ Web Storage อย่างหนักจึงนำไปสู่ frame-time delays ที่วัดได้
// ทุก call เหล่านี้เป็น synchronous — thread จะ block จนแต่ละอันเสร็จconst start = performance.now();for (let i = 0; i < 1000; i++) { localStorage.setItem('key-' + i, 'value-' + i);}const elapsed = performance.now() - start;console.log('1 000 writes took ' + elapsed.toFixed(2) + ' ms');Asynchronous storage: IndexedDB และ Cache API
หัวข้อที่มีชื่อว่า “Asynchronous storage: IndexedDB และ Cache API”IndexedDB และ Cache API เป็น asynchronous ทุก operation คืน Promise (หรือยิง event) และโค้ดของคุณดำเนินต่อทันที I/O จริงเกิดขึ้นนอก main thread เมื่อผลลัพธ์พร้อม browser จะเรียก callback หรือ resolve Promise ของคุณ
// return ทันที — การเขียนเกิดขึ้นใน backgroundconst db = await idb.openDB('my-db', 1, { upgrade(db) { db.createObjectStore('items'); }});await db.put('items', { name: 'Alice' }, 'user-1');console.log('Write complete — main thread was never blocked');โมเดลสองเลน
หัวข้อที่มีชื่อว่า “โมเดลสองเลน”flowchart LR
subgraph Sync["Synchronous (Web Storage / Cookies)"]
direction TB
T1[Main thread] -->|blocked| R1[localStorage.getItem]
R1 -->|returns| T1
end
subgraph Async["Asynchronous (IndexedDB / Cache API)"]
direction TB
T2[Main thread] -->|fires request| IDB[IndexedDB / Cache]
T2 -->|continues immediately| T2
IDB -->|Promise resolves| T2
end Runnable timing comparison
หัวข้อที่มีชื่อว่า “Runnable timing comparison”snippet ด้านล่างนับเวลา localStorage.setItem จำนวน 10 000 ครั้งแบบ synchronous บันทึกเวลาที่ผ่านไป จากนั้นทำความสะอาด key ที่สร้างขึ้นทั้งหมดที่ขึ้นต้นด้วย demo:
ข้อแลกเปลี่ยน
หัวข้อที่มีชื่อว่า “ข้อแลกเปลี่ยน”| ตัวเลือก | Benefit | Cost |
|---|---|---|
| Synchronous (Web Storage / cookies) | API เรียบง่าย เขียนโค้ดบรรทัดเดียวได้ผลลัพธ์ทันที ไม่ต้อง await | block main thread ทุกครั้งที่เรียก ทำให้เกิด jank เมื่อข้อมูลใหญ่หรือเรียกถี่ |
| Asynchronous (IndexedDB / Cache API) | ไม่ block main thread รองรับข้อมูลขนาดใหญ่และ operation จำนวนมากได้อย่างลื่นไหล | โค้ดซับซ้อนขึ้น ต้องจัดการ Promise/callback และ error handling เพิ่ม |
OPFS createSyncAccessHandle() ใน Worker | ให้ synchronous I/O ที่เร็วมากโดยไม่กระทบ main thread | ใช้ได้เฉพาะใน Web Worker เท่านั้น ต้องออกแบบสถาปัตยกรรมแยก thread ตั้งแต่ต้น |
ข้อผิดพลาดที่พบบ่อย
หัวข้อที่มีชื่อว่า “ข้อผิดพลาดที่พบบ่อย”- วนลูปเขียน
localStorageจำนวนมากใน hot path — เช่นทุก keystroke หรือ scroll event ทำให้ main thread ถูก block ซ้ำ ๆ จนเกิด jank ที่ผู้ใช้สัมผัสได้จริง - คิดว่า async แปลว่าเร็วกว่าเสมอ — asynchronous storage ไม่ block thread ก็จริง แต่ operation เดี่ยว ๆ อาจใช้เวลานานกว่า synchronous call เล็กน้อยเพราะมี overhead ของ event loop และ IPC
- ลืมว่า
document.cookieก็เป็น synchronous เหมือนกัน — หลายคนโฟกัสแค่localStorageแต่การ parse cookie string ยาว ๆ บน hot code path ก็ block main thread ไม่ต่างกัน
💡 ตัวอย่างจากของจริง
Gmail offline mode — ใช้ IndexedDB (async) เก็บ email ที่ sync ไว้สำหรับใช้งาน offline เพราะข้อมูลมีปริมาณมากเกินกว่าที่ synchronous API จะรับมือได้โดยไม่ทำให้ UI ค้าง
Twitter Lite (PWA) — เลือกใช้ IndexedDB และ Cache API แทน
localStorageสำหรับ cache ข้อมูล timeline เพื่อให้ scrolling และ interaction ยังคงลื่นไหลแม้ cache จะมีขนาดใหญ่