ทำไมแอปพลิเคชันต้องมีระบบ Real-Time
เวลาเราใช้งานแอปแชทหรือแอปสั่งอาหาร เรามักจะเห็นสถานะที่อัปเดตทันทีโดยไม่ต้องกดรีเฟรชหน้าจอ ซึ่งความสามารถนี้เรียกว่า Real-Time (การอัปเดตข้อมูลแบบทันทีทันใด) ในฐานะนักพัฒนาหน้าบ้าน หรือ Frontend Developer (นักพัฒนาส่วนที่ผู้ใช้มองเห็นและโต้ตอบ) เรามักจะคิดถึงการเชื่อมต่อที่รวดเร็วและต่อเนื่องเป็นอันดับแรกเสมอ
แต่การทำระบบให้ข้อมูลอัปเดตตลอดเวลาไม่ได้มีแค่ทางเดียว และไม่ใช่ทุกงานที่ต้องใช้เครื่องมือที่ซับซ้อนที่สุด บางครั้งวิธีที่ง่ายที่สุดก็เพียงพอต่อความต้องการของผู้ใช้งานแล้ว การเลือกเครื่องมือให้เหมาะสมกับงานจึงเป็นทักษะสำคัญที่โปรแกรมเมอร์มืออาชีพต้องฝึกฝน เพื่อประหยัดทรัพยากรของเซิร์ฟเวอร์และทำให้แอปของเราทำงานได้ลื่นไหล
ก่อนจะตัดสินใจเลือกเทคโนโลยี เราต้องเข้าใจก่อนว่าข้อมูลที่แอปเราต้องการนั้นมีลักษณะการไหลอย่างไร เพราะถ้าเข้าใจธรรมชาติของมันแล้ว เราจะรู้ทันทีว่าควรใช้ Polling (การถามข้อมูลซ้ำๆ), SSE (การรับข้อมูลจากเซิร์ฟเวอร์แบบทางเดียว), หรือ WebSocket (การสื่อสารสองทาง) เพื่อให้แอปของเราทำงานได้มีประสิทธิภาพสูงสุด
1. Polling: วิธีที่ง่ายที่สุดในการถามข้อมูล
Polling (การวนลูปถามข้อมูลจากเซิร์ฟเวอร์เป็นระยะ) คือวิธีที่พื้นฐานที่สุด เหมือนกับการที่เราเดินไปถามเพื่อนทุกๆ 5 นาทีว่า "งานเสร็จหรือยัง" โดยเราจะใช้การส่งคำขอแบบ HTTP Request (การส่งคำสั่งขอข้อมูลผ่านโปรโตคอลเว็บ) ออกไปเรื่อยๆ ตามเวลาที่กำหนดไว้จนกว่าจะได้ผลลัพธ์ที่ต้องการ
วิธีนี้เหมาะมากสำหรับงานที่ไม่ต้องอัปเดตวินาทีต่อวินาที เช่น การเช็กสถานะการจ่ายเงิน หรือการดูความคืบหน้าของการทำรายการในระบบหลังบ้าน ข้อดีคือมันทำง่ายและไม่ต้องตั้งค่าฝั่งเซิร์ฟเวอร์ให้ซับซ้อน เพราะเป็นการใช้คำสั่งเดิมๆ ที่เรามีอยู่แล้ว แต่ข้อเสียคือถ้าคนใช้งานจำนวนมากพร้อมกัน จะทำให้เซิร์ฟเวอร์ทำงานหนักโดยไม่จำเป็น
เรามักจะใช้ setTimeout เพื่อรอให้คำขอแรกจบก่อนแล้วค่อยส่งอันถัดไป เพื่อป้องกันไม่ให้คำขอค้างสะสมกันจนเว็บล่ม หรือจะใช้เครื่องมืออย่าง TanStack Query (เครื่องมือจัดการสถานะข้อมูลในแอป) มาช่วยจัดการให้โค้ดสะอาดขึ้นก็ได้ ซึ่งเป็นวิธีที่ได้รับความนิยมมากในหมู่โปรแกรมเมอร์ปัจจุบัน
// ตัวอย่างการทำ Polling แบบง่ายด้วย useEffect
useEffect(() => {
let timeoutId;
async function poll() {
const response = await fetch('/api/jobs/42'); // ส่งคำขอไปที่เซิร์ฟเวอร์
const job = await response.json(); // แปลงข้อมูลที่ได้เป็น JSON
setJob(job); // อัปเดตข้อมูลในหน้าจอ
if (job.status === 'processing') {
timeoutId = setTimeout(poll, 2000); // ถ้างานยังไม่เสร็จ ให้รอ 2 วินาทีแล้วถามใหม่
}
}
poll();
return () => clearTimeout(timeoutId); // ยกเลิกการทำงานเมื่อคอมโพเนนต์ถูกปิด
}, [jobId]);
คำอธิบายโค้ด:
1. fetch ใช้ส่งคำขอไปดึงข้อมูลจากเซิร์ฟเวอร์
2. setTimeout สั่งให้ฟังก์ชัน poll ทำงานซ้ำหลังจากผ่านไป 2 วินาที
3. clearTimeout ใช้สำหรับหยุดการถามข้อมูลเมื่อผู้ใช้เปลี่ยนหน้า หรือปิดแอปไปแล้ว เพื่อป้องกันบั๊ก
ผลลัพธ์ที่ควรเห็น: หน้าจอจะแสดงสถานะงานที่ค่อยๆ เปลี่ยนจาก "processing" เป็น "completed" โดยที่ผู้ใช้ไม่ต้องกดปุ่มรีเฟรชหน้าเว็บเองเลย
2. Server-Sent Events (SSE): เมื่อเซิร์ฟเวอร์เป็นฝ่ายบอกเราเอง
Server-Sent Events หรือ SSE (การเปิดช่องทางให้เซิร์ฟเวอร์ส่งข้อความมาหาเราฝ่ายเดียว) เปลี่ยนวิธีคิดจากการที่เราต้องคอยถาม เป็นการเปิดท่อเชื่อมต่อไว้แล้วรอให้เซิร์ฟเวอร์ยิงข้อมูลมาบอกเมื่อมีอะไรเปลี่ยนแปลง วิธีนี้เหมาะมากสำหรับงานจำพวกแจ้งเตือน หรือไลฟ์ฟีดข่าวสารที่ข้อมูลฝั่งเซิร์ฟเวอร์มีการเปลี่ยนแปลงอยู่ตลอดเวลา
เบื้องหลังของ SSE คือการเชื่อมต่อแบบ HTTP Stream (การส่งข้อมูลแบบต่อเนื่องผ่านท่อเดียว) ซึ่งเบราว์เซอร์มีเครื่องมือที่ชื่อว่า EventSource เตรียมไว้ให้เราใช้ได้ทันที การเชื่อมต่อแบบนี้จะประหยัดพลังงานกว่า Polling มาก เพราะเซิร์ฟเวอร์จะส่งข้อมูลมาเมื่อมีเหตุการณ์จริงเท่านั้น ไม่ต้องมีคำถามจากฝั่งเราคอยรบกวนตลอดเวลา
ข้อจำกัดที่ควรรู้คือ SSE เป็นการสื่อสารทางเดียว คือเซิร์ฟเวอร์ส่งมาให้เราได้ แต่เราส่งกลับไปไม่ได้ผ่านท่อนั้น ถ้าเราต้องการส่งคำสั่งกลับไป เราก็แค่ใช้วิธี POST Request (การส่งข้อมูลไปที่เซิร์ฟเวอร์) ตามปกติ ซึ่งเป็นการผสมผสานที่ลงตัวสำหรับแอปหลายประเภทที่เน้นการรับข้อมูลเป็นหลัก
// ตัวอย่างการใช้ EventSource รับข้อมูลจากเซิร์ฟเวอร์
useEffect(() => {
const source = new EventSource('/api/orders/updates'); // เปิดการเชื่อมต่อแบบต่อเนื่อง
source.addEventListener('order.updated', (event) => {
const data = JSON.parse(event.data); // แปลงข้อมูลที่ได้รับจากเซิร์ฟเวอร์
console.log('ข้อมูลอัปเดต:', data);
});
return () => source.close(); // ปิดการเชื่อมต่อเมื่อเลิกใช้งาน
}, []);
คำอธิบายโค้ด:
1. EventSource สร้างการเชื่อมต่อถาวรกับเซิร์ฟเวอร์
2. addEventListener คอยดักฟังเหตุการณ์ที่เซิร์ฟเวอร์ส่งมา
3. source.close() คือการทำความสะอาดการเชื่อมต่อ เพื่อไม่ให้เปลืองทรัพยากรเมื่อเลิกใช้งาน
ผลลัพธ์ที่ควรเห็น: เมื่อเซิร์ฟเวอร์ส่งข้อมูลอัปเดตสถานะคำสั่งซื้อมา หน้าเว็บจะแสดงข้อมูลใหม่ทันทีโดยไม่มีการหน่วงเวลาเหมือนการทำ Polling
3. WebSocket: สื่อสารสองทางแบบเต็มรูปแบบ
WebSocket (โปรโตคอลการสื่อสารแบบสองทางที่เชื่อมต่อค้างไว้) คือตัวเลือกที่ทรงพลังที่สุดเมื่อเราต้องการแอปที่โต้ตอบกันไปมาได้ทันที เช่น แอปแชท หรือเกมออนไลน์ เพราะทั้งฝั่งเราและฝั่งเซิร์ฟเวอร์สามารถส่งข้อมูลหากันได้ตลอดเวลาโดยไม่มีข้อจำกัดเรื่องทิศทาง
การใช้ WebSocket เหมือนกับการเปิดสายโทรศัพท์ทิ้งไว้ ต่างจากการส่งจดหมายที่ต้องรอรับและรอส่ง มันให้ความรู้สึกที่เร็วและตอบสนองดีเยี่ยม แต่ก็แลกมาด้วยความซับซ้อนในการจัดการ เพราะเราต้องดูแลทั้งเรื่องการเชื่อมต่อที่อาจหลุด การยืนยันตัวตน และการจัดการสถานะของข้อความให้ครบถ้วน
ในการทำงานจริง การเขียนระบบ WebSocket ด้วยตัวเองตั้งแต่ศูนย์นั้นยากและมีโอกาสเกิดบั๊กสูง โปรแกรมเมอร์ส่วนใหญ่จึงนิยมใช้ไลบรารีหรือเครื่องมือเสริมเข้ามาช่วยจัดการเรื่องการเชื่อมต่อให้เสถียรขึ้น หากคุณกำลังจะทำโปรเจกต์แชท นี่คือเทคโนโลยีที่ตอบโจทย์ที่สุด แต่ต้องเตรียมรับมือกับความซับซ้อนที่ตามมาด้วย
// ตัวอย่างการเชื่อมต่อ WebSocket
const socket = new WebSocket('wss://example.com/chat');
socket.onmessage = (event) => {
const message = JSON.parse(event.data); // รับข้อความจากเซิร์ฟเวอร์
console.log('ได้รับข้อความ:', message);
};
// ส่งข้อความไปที่เซิร์ฟเวอร์
socket.send(JSON.stringify({ text: 'สวัสดีครับ!' }));
คำอธิบายโค้ด:
1. new WebSocket ใช้เชื่อมต่อไปยังที่อยู่ของเซิร์ฟเวอร์
2. onmessage คือฟังก์ชันที่จะทำงานทันทีเมื่อได้รับข้อมูลจากฝั่งเซิร์ฟเวอร์
3. socket.send ใช้สำหรับส่งข้อมูลกลับไปยังเซิร์ฟเวอร์ผ่านท่อเดียวกัน
ผลลัพธ์ที่ควรเห็น: ข้อความที่พิมพ์จะถูกส่งและรับได้ทันที ทั้งฝั่งเราและฝั่งเพื่อนคุยจะเห็นข้อความปรากฏขึ้นพร้อมกันโดยไม่ต้องรอ
เลือกเทคโนโลยีไหนให้เหมาะกับโปรเจกต์
การเลือกใช้เทคโนโลยีขึ้นอยู่กับว่าข้อมูลของคุณมีความสำคัญแค่ไหนและต้องเร็วเพียงใด หากคุณเป็นมือใหม่ที่เพิ่งเริ่มต้นทำโปรเจกต์แรก แนะนำให้เริ่มจาก Polling ก่อน เพราะเข้าใจง่ายและดีบั๊ก (แก้ไขข้อผิดพลาด) ได้สะดวกที่สุด หากเริ่มรู้สึกว่ามันช้าไปค่อยขยับไปใช้ SSE ครับ
เมื่อคุณเริ่มทำแอปประเภท Dashboard (หน้าจอสรุปข้อมูล) ที่ต้องโชว์กราฟวิ่งตลอดเวลา SSE จะเป็นตัวเลือกที่ยอดเยี่ยมมาก เพราะมันช่วยลดภาระของเซิร์ฟเวอร์ได้มหาศาลเมื่อเทียบกับ Polling และยังไม่ต้องปวดหัวกับการจัดการการเชื่อมต่อที่ซับซ้อนเท่ากับ WebSocket ในช่วงแรกของการเรียนรู้
สุดท้ายถ้าโปรเจกต์ของคุณคือแอปแชทหรือเครื่องมือที่ต้องทำงานร่วมกันแบบเรียลไทม์ WebSocket คือคำตอบที่หลีกเลี่ยงไม่ได้ อย่าลืมฝึกฝนการเขียนโค้ดที่จัดการเรื่องการหลุดของการเชื่อมต่อด้วย เพราะในโลกความเป็นจริง เครือข่ายอินเทอร์เน็ตไม่ได้เสถียรตลอดเวลา และการจัดการส่วนนี้คือสิ่งที่แยกโปรแกรมเมอร์มือสมัครเล่นออกจากมืออาชีพ
บทสรุป: เริ่มต้นก้าวแรกให้ถูกทาง
การเข้าใจความแตกต่างระหว่าง Polling, SSE และ WebSocket จะช่วยให้คุณประหยัดเวลาในการพัฒนาและทำให้แอปมีประสิทธิภาพดีขึ้น จำไว้ว่า เครื่องมือที่ดีที่สุดคือเครื่องมือที่เหมาะสมกับงานมากที่สุด ไม่ใช่เครื่องมือที่ทันสมัยที่สุดในตลาดเสมอไป เริ่มต้นจากงานเล็กๆ แล้วค่อยขยายขอบเขตการเรียนรู้ไปตามความต้องการของโปรเจกต์
สำหรับคนที่กำลังเตรียมตัวเป็นโปรแกรมเมอร์ ลองนำวิธีเหล่านี้ไปปรับใช้ในโปรเจกต์ส่วนตัว เช่น ถ้าคุณทำแอปติดตามราคาสินค้า ลองทำ Polling ก่อน แล้วค่อยลองเปลี่ยนเป็น SSE เพื่อดูความต่างของปริมาณข้อมูลที่รับส่ง วิธีนี้จะช่วยให้คุณเห็นภาพชัดเจนขึ้นว่าแต่ละเทคโนโลยีมีผลต่อการทำงานของแอปอย่างไร
เมื่อคุณเข้าใจพื้นฐานการสื่อสารเหล่านี้แล้ว คุณจะสามารถออกแบบระบบที่รองรับผู้ใช้งานจำนวนมากได้อย่างมั่นใจ การเป็นโปรแกรมเมอร์เก่งๆ ไม่ได้วัดกันที่ว่าใช้เครื่องมือเป็นกี่ตัว แต่วัดกันที่ว่าคุณเลือกใช้เครื่องมือเหล่านั้นอย่างฉลาดเพื่อแก้ปัญหาให้ผู้ใช้งานได้ดีแค่ไหนครับ
ที่มา: WebSocket vs SSE vs Polling: A Frontend Developer’s Guide to Real-Time Updates — DEV Community