← กรณีการใช้งาน

เลิกโพลลิงแล้วใช้การอัปเดตแบบเรียลไทม์

เลิกถาม API ทุกไม่กี่วินาที ส่งการเปลี่ยนแปลงแต่ละครั้งเพียงครั้งเดียว ลดคำขอและภาระเซิร์ฟเวอร์ และแสดงอัปเดตทันทีที่เกิดขึ้น

หลายแอปรู้ว่ามีอะไรเปลี่ยนด้วยการ "ถาม": ทุกหน้าที่เปิดอยู่จะเรียก API ทุกไม่กี่วินาทีเพื่อดูว่าคำสั่งซื้อ ทิกเก็ต หรือรายงานเปลี่ยนหรือยัง คำตอบแทบทุกครั้งคือ "ไม่มีอะไรใหม่" แต่ทุกครั้งต้องเสียคำขอหนึ่งครั้ง คิวรีฐานข้อมูลหนึ่งครั้ง และแบตเตอรี่มือถือของผู้ใช้ ส่วนการเปลี่ยนแปลงจริงก็ยังต้องรอถึงรอบถัดไปกว่าจะเห็น เมื่อใช้ Hibira เซิร์ฟเวอร์จะส่งการเปลี่ยนแปลงเพียงครั้งเดียวตอนที่มันเกิดขึ้น และทุกหน้าที่แสดงข้อมูลนั้นจะอัปเดตภายในไม่กี่มิลลิวินาที

ผู้ใช้ของคุณได้อะไร

  • เห็นการเปลี่ยนแปลงทันทีที่เกิดขึ้น ไม่ต้องรอสูงสุดห้าวินาที
  • ทุกแท็บเป็นข้อมูลล่าสุดเสมอโดยไม่ต้องรีโหลด
  • มือถือไม่ต้องปลุกสัญญาณเครือข่ายทุกไม่กี่วินาทีโดยเปล่าประโยชน์

ต้นทุนของการโพล

ผู้ใช้ 1,000 คนเปิดหน้าไว้และโพลทุก 5 วินาที เท่ากับ 720,000 คำขอต่อชั่วโมง ถ้าในชั่วโมงนั้นข้อมูลเปลี่ยนเพียง 200 ครั้ง คำขอ 99.97% จะไม่มีอะไรใหม่เลย เมื่อเปลี่ยนเป็นการพุช ชั่วโมงเดียวกันใช้เพียง 200 ข้อความ และ API ของคุณรับแค่การโหลดครั้งแรกของแต่ละหน้า

สร้างด้วย

  • ช่องส่วนตัว (private-order-42) หนึ่งช่องต่อสิ่งที่หน้าเว็บแสดง เซิร์ฟเวอร์ของคุณเป็นผู้อนุญาต ผู้ใช้จึงได้รับเฉพาะข้อมูลของตัวเอง
  • trigger ใน SDK ฝั่งเซิร์ฟเวอร์อย่างเป็นทางการ เรียกทันทีหลังบันทึกการเปลี่ยนแปลง
  • pusher:subscription_succeeded ซึ่งเกิดขึ้นตอนเชื่อมต่อครั้งแรกและทุกครั้งหลังเชื่อมต่อใหม่ จึงเป็นจังหวะที่เหมาะกับการโหลดสถานะปัจจุบัน

การโพล

ทุกหน้าที่เปิดอยู่ทุก 5 วินาที
GET /api/orders/42
API ของคุณส่วนใหญ่ "ไม่มีอะไรใหม่"

การพุช

API ของคุณบันทึกการเปลี่ยนแปลง
trigger() ครั้งเดียว
Hibira
WebSocket
ทุกหน้าที่แสดงข้อมูลนี้อัปเดตทันที
การโพลถามซ้ำแล้วซ้ำอีก การพุชส่งแต่ละการเปลี่ยนแปลงครั้งเดียว

ขั้นตอนที่ 1: หาลูปที่โพลอยู่

ปกติจะหน้าตาแบบนี้:

setInterval(async () => {
  render(await fetch('/api/orders/42').then((r) => r.json()));
}, 5000);

ขั้นตอนที่ 2: โหลดครั้งเดียว แล้วรอฟัง

เชื่อมต่อตามคู่มือ JavaScript พร้อมปลายทางอนุญาตช่องสำหรับช่องส่วนตัว โหลดสถานะปัจจุบันเมื่อการสมัครพร้อม แล้วนำการเปลี่ยนแปลงแต่ละครั้งไปใช้เมื่อมาถึง:

const load = async () => render(await fetch('/api/orders/42').then((r) => r.json()));

const channel = pusher.subscribe('private-order-42');
channel.bind('pusher:subscription_succeeded', load);
channel.bind('updated', (order) => render(order));

ขั้นตอนที่ 3: ส่งการเปลี่ยนแปลงตอนบันทึก

ไม่ว่าข้อมูลจะเปลี่ยนที่ไหน (route ของ API, worker, การกระทำของแอดมิน, webhook) ให้ส่งหลังเขียนสำเร็จ:

app.patch('/api/orders/:id', requireLogin, async (req, res) => {
  const order = await db.orders.update(req.params.id, req.body);
  await pusher.trigger(`private-order-${order.id}`, 'updated', order);
  res.json(order);
});

ขั้นตอนที่ 4: มีตาข่ายนิรภัยเบา ๆ

เพราะการสมัครจะเกิดขึ้นอีกครั้งหลังเชื่อมต่อใหม่ load() จึงเติมช่องว่างช่วงที่หลุดการเชื่อมต่อให้แล้ว เบราว์เซอร์ยังอาจหยุดแท็บที่อยู่เบื้องหลัง จึงควรโหลดอีกครั้งเมื่อแท็บกลับมาแสดง:

document.addEventListener('visibilitychange', () => {
  if (document.visibilityState === 'visible') load();
});

เคล็ดลับ

  • ส่งเฉพาะสิ่งที่หน้าเว็บต้องใช้วาดใหม่ ถ้าข้อมูลใหญ่ให้ส่งแค่ ID กับเวอร์ชัน แล้วให้หน้าเว็บดึงส่วนที่เหลือเอง
  • ข้อมูลเดียวกันเปลี่ยนหลายครั้งต่อวินาที? รวมเป็นชุดแบบในแดชบอร์ดเรียลไทม์
  • รายการที่เปลี่ยนบ่อยใช้หนึ่งช่องต่อรายการ (private-orders-team-7) แทนหนึ่งช่องต่อแถวได้
  • ขนาดของอีเวนต์ขึ้นอยู่กับแพ็กเกจ ดูที่ราคา
  • อยากรู้ว่ามีคนดูอยู่กี่คนก่อนส่ง? ดูที่จำนวนผู้ใช้ออนไลน์