BroadcastChannel
BroadcastChannel คืออะไร?
หัวข้อที่มีชื่อว่า “BroadcastChannel คืออะไร?”BroadcastChannel เป็น browser API ที่ให้ message bus แบบ publish/subscribe ที่ scoped ไว้กับ origin เดียวกัน แท็บ, iframe หรือ worker ใดก็ตามที่สร้าง channel ด้วยชื่อเดียวกันจะเข้าร่วมกลุ่มเดียวกันโดยอัตโนมัติ เมื่อสมาชิกหนึ่งเรียก postMessage สมาชิกอื่นทุกคนจะได้รับข้อความผ่าน handler onmessage
มีความคล้ายคลึงกับ storage event ของ localStorage แต่มีความแตกต่างที่สำคัญ:
| คุณสมบัติ | BroadcastChannel | storage event |
|---|---|---|
| เนื้อหาข้อความ | ค่าใดก็ได้ที่ structured-cloneable | String เท่านั้น (storage value) |
| ทำงานใน workers | ใช่ (Shared/Service Workers) | ไม่ |
| เกิดในแท็บที่ส่ง | ไม่ | ไม่ |
| ต้องเขียน localStorage ก่อน | ไม่ — ส่งตรง | ใช่ |
การสร้างและใช้งาน channel
หัวข้อที่มีชื่อว่า “การสร้างและใช้งาน channel”// Create (or join) a channel by nameconst ch = new BroadcastChannel('my-channel');
// Listen for messagesch.onmessage = (event) => { console.log('Got:', event.data);};
// Send a message to all other membersch.postMessage({ type: 'UPDATE', payload: 42 });
// When done, release resourcesch.close();ชื่อเป็นตัวระบุเพียงอย่างเดียว — สองหน้าบน origin เดียวกันที่ใช้ new BroadcastChannel('chat') จะเป็น peer กันโดยอัตโนมัติ ไม่มีขั้นตอน server หรือการลงทะเบียน
รูปแบบ event listener
หัวข้อที่มีชื่อว่า “รูปแบบ event listener”ใช้ addEventListener แทน onmessage ก็ได้ ทำให้แนบ listener หลายตัวได้:
const ch = new BroadcastChannel('notifications');
ch.addEventListener('message', (e) => { console.log('Handler 1:', e.data);});
ch.addEventListener('message', (e) => { console.log('Handler 2:', e.data);});การปิด channel
หัวข้อที่มีชื่อว่า “การปิด channel”เรียก ch.close() เมื่อไม่ต้องการรับข้อความอีก channel ไม่ใช่ shared state — การปิดในแท็บหนึ่งไม่มีผลต่อแท็บอื่น
const ch = new BroadcastChannel('demo:temp');// ... use it ...ch.close(); // releases internal resourcesรันได้: post และรับบน channel เดียวกัน
หัวข้อที่มีชื่อว่า “รันได้: post และรับบน channel เดียวกัน”ตัวอย่างด้านล่างสร้าง channel แนบ listener ส่งข้อความ และปิดหลังรับข้อความ เพราะ sender และ receiver อยู่ในหน้าเดียวกัน ข้อความจึงสะท้อนกลับ — ในแอปจริง listener อยู่ในแท็บแยก
ข้อแลกเปลี่ยน
หัวข้อที่มีชื่อว่า “ข้อแลกเปลี่ยน”| ตัวเลือก | Benefit | Cost |
|---|---|---|
BroadcastChannel | ส่งข้อความแบบ real-time ไปยังทุกแท็บ/worker ที่ join channel เดียวกัน โดยไม่ต้องเขียนลง storage ก่อน | ไม่มี built-in persistence — ถ้าไม่มีแท็บใด listen อยู่ตอนส่ง ข้อความจะหายไปเลย |
storage event | ใช้ persistence layer ที่มีอยู่แล้ว (localStorage) เป็น side effect โดยไม่ต้องตั้ง channel เพิ่ม | ส่งได้แค่ string และไม่ยิงในแท็บที่เขียนเอง ต้อง serialize/deserialize เอง |
window.postMessage ข้าม window reference | ควบคุม target ได้ละเอียด ส่งเฉพาะ window ที่มี reference อยู่ | ต้องถือ reference ของแต่ละ window ไว้เอง ใช้ยากเมื่อมีหลายแท็บที่เปิด-ปิดแบบไดนามิก |
ข้อผิดพลาดที่พบบ่อย
หัวข้อที่มีชื่อว่า “ข้อผิดพลาดที่พบบ่อย”- คิดว่า BroadcastChannel เก็บข้อความไว้รอผู้รับที่ join ทีหลัง — ต้อง join channel ก่อนส่งข้อความเสมอ ถ้าแท็บผู้รับยังไม่ได้สร้าง
BroadcastChannelในตอนที่ส่ง ข้อความนั้นจะหายไปเงียบๆ - ลืมเรียก
ch.close()เมื่อ component unmount — channel object และ listener จะค้างอยู่ใน memory กลายเป็น leak โดยเฉพาะใน SPA ที่ mount/unmount บ่อย - คาดหวังว่าแท็บที่เรียก
postMessageจะได้รับ event ของตัวเองด้วย — BroadcastChannel ไม่ส่งข้อความกลับไปยัง sender เหมือนกับที่storageevent ก็ไม่ยิงในแท็บที่เขียน ต้องอัปเดต UI ของแท็บที่ส่งเองแยกต่างหาก
💡 ตัวอย่างจากของจริง
Google Docs — ใช้ pattern คล้าย BroadcastChannel เพื่อ sync สถานะการแก้ไข เช่น cursor, selection และ presence indicator ข้ามแท็บที่เปิดเอกสารเดียวกัน
Slack web — ใช้ cross-tab messaging เพื่อให้ notification badge และ unread count sync กันทุกแท็บ โดยไม่ต้อง poll server ซ้ำในแต่ละแท็บ