# Thay polling bằng cập nhật theo thời gian thực

> Thôi hỏi API vài giây một lần: gửi mỗi thay đổi đúng một lần, giảm yêu cầu và tải máy chủ, và hiện cập nhật ngay khi nó xảy ra.

Source: https://hibira.io/vi/use-cases/replace-polling

Nhiều ứng dụng biết có thay đổi bằng cách "hỏi": mỗi trang đang mở gọi API vài giây một lần để xem đơn hàng, ticket hay báo cáo đã đổi chưa. Gần như lần nào câu trả lời cũng là "không có gì mới", nhưng mỗi lần đều tốn một yêu cầu, một truy vấn cơ sở dữ liệu và pin điện thoại của người dùng; còn thay đổi thật thì vẫn phải chờ đến lượt hỏi sau mới thấy. Với Hibira, máy chủ gửi mỗi thay đổi đúng một lần khi nó xảy ra, và mọi trang đang hiển thị dữ liệu đó cập nhật trong vài mili giây.

## Người dùng nhận được gì

- Thay đổi hiện lên ngay khi xảy ra, không phải chờ tới năm giây.
- Mọi tab luôn mới nhất mà không cần tải lại.
- Điện thoại không còn đánh thức mạng vài giây một lần chỉ để nhận câu "không có gì mới".

## Cái giá của polling

1.000 người mở trang và hỏi 5 giây một lần tạo ra 720.000 yêu cầu mỗi giờ. Nếu trong giờ đó dữ liệu chỉ đổi 200 lần, 99,97% số yêu cầu không có gì mới. Chuyển sang đẩy, cùng giờ đó chỉ cần 200 tin nhắn, và API của bạn chỉ phục vụ lần tải đầu tiên của mỗi trang.

## Xây dựng bằng

- **Kênh riêng tư** (`private-order-42`), mỗi thứ trang hiển thị một kênh, do máy chủ của bạn cấp quyền nên người dùng chỉ nhận dữ liệu của chính mình.
- **`trigger`** trong các SDK máy chủ chính thức, gọi ngay sau khi lưu thay đổi.
- **`pusher:subscription_succeeded`**, kích hoạt ở lần kết nối đầu và sau mỗi lần kết nối lại: đúng lúc để tải trạng thái hiện tại.

```text
flow: Polling hỏi đi hỏi lại; đẩy gửi mỗi thay đổi một lần
== Polling
Mọi trang đang mở|mỗi 5 giây --GET /api/orders/42--> API của bạn|phần lớn "không có gì mới"
== Đẩy
API của bạn|lưu thay đổi --`trigger()` một lần--> *Hibira --WebSocket--> Mọi trang hiển thị nó|cập nhật ngay
```

## Bước 1: tìm vòng lặp polling

Thường trông như sau:

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

## Bước 2: tải một lần, rồi lắng nghe

Kết nối như trong [hướng dẫn JavaScript](https://hibira.io/vi/guides/javascript), kèm endpoint cấp quyền kênh cho kênh riêng tư. Tải trạng thái hiện tại khi đăng ký sẵn sàng, rồi áp dụng từng thay đổi khi nó đến:

```js
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));
```

## Bước 3: gửi thay đổi khi lưu

Dữ liệu đổi ở đâu (route API, worker, thao tác quản trị, webhook) thì phát ngay sau khi ghi thành công:

```js
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);
});
```

## Bước 4: giữ một lưới an toàn nhẹ

Vì đăng ký sẽ kích hoạt lại sau khi kết nối lại, `load()` đã lấp khoảng trống lúc mất kết nối. Trình duyệt cũng có thể đóng băng tab nền, nên hãy tải lại một lần khi tab hiện trở lại:

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

## Mẹo

- Gửi những gì trang cần để vẽ lại; với bản ghi lớn chỉ gửi ID và phiên bản, để trang tự lấy phần còn lại.
- Cùng một dữ liệu đổi nhiều lần mỗi giây? Gộp lại như trong [dashboard thời gian thực](https://hibira.io/vi/use-cases/live-dashboards).
- Danh sách thay đổi thường xuyên có thể dùng một kênh cho cả danh sách (`private-orders-team-7`) thay vì một kênh mỗi dòng.
- Kích thước sự kiện tùy theo gói, xem [bảng giá](https://hibira.io/vi/pricing).
- Muốn biết có bao nhiêu người đang xem trước khi phát? Xem [số người đang online](https://hibira.io/vi/use-cases/online-user-counts).
