Bật các chức năng ngoại tuyến trong JavaScript

Ứng dụng Firebase hoạt động ngay cả khi ứng dụng của bạn tạm thời mất kết nối mạng. Chúng tôi cung cấp một số công cụ giúp theo dõi sự hiện diện và đồng bộ hoá trạng thái cục bộ với trạng thái máy chủ. Những công cụ này được giới thiệu trong tài liệu này.

Quản lý sự hiện diện

Trong các ứng dụng thời gian thực, thường hữu ích trong việc phát hiện khi nào ứng dụng kết nối và ngắt kết nối. Ví dụ: bạn có thể muốn đánh dấu người dùng là 'ngoại tuyến' khi khách hàng của họ ngắt kết nối.

Ứng dụng Cơ sở dữ liệu Firebase cung cấp các dữ liệu gốc đơn giản mà bạn có thể sử dụng để ghi vào cơ sở dữ liệu khi ứng dụng ngắt kết nối khỏi Cơ sở dữ liệu Firebase máy chủ. Các quá trình cập nhật này diễn ra dù máy khách có ngắt kết nối một cách dễ dàng hay không, để bạn có thể dựa vào các dịch vụ này để dọn dẹp dữ liệu ngay cả khi mất kết nối hoặc ứng dụng gặp sự cố. Tất cả thao tác ghi, bao gồm cả thao tác đặt, cập nhật và xoá có thể được thực hiện khi ngắt kết nối.

Dưới đây là một ví dụ đơn giản về cách ghi dữ liệu khi bị ngắt kết nối bằng cách sử dụng Dữ liệu gốc onDisconnect:

Web

import { getDatabase, ref, onDisconnect } from "firebase/database";

const db = getDatabase();
const presenceRef = ref(db, "disconnectmessage");
// Write a string when this client loses connection
onDisconnect(presenceRef).set("I disconnected!");

Web

var presenceRef = firebase.database().ref("disconnectmessage");
// Write a string when this client loses connection
presenceRef.onDisconnect().set("I disconnected!");

Cách tính năng onNgắt kết nối hoạt động

Khi bạn thiết lập một toán tử onDisconnect(), toán tử có trên máy chủ Cơ sở dữ liệu theo thời gian thực Firebase. Máy chủ kiểm tra bảo mật để đảm bảo người dùng có thể thực hiện sự kiện ghi theo yêu cầu và thông báo ứng dụng của bạn nếu ứng dụng đó không hợp lệ. Sau đó, máy chủ sẽ giám sát kết nối. Nếu tại bất kỳ thời điểm nào kết nối hết thời gian chờ, hoặc được chủ động đóng bởi ứng dụng Cơ sở dữ liệu theo thời gian thực, máy chủ sẽ kiểm tra bảo mật lần thứ hai (để đảm bảo thao tác vẫn hợp lệ) rồi gọi sự kiện.

Ứng dụng của bạn có thể dùng lệnh gọi lại trong thao tác ghi để đảm bảo onDisconnect đã được đính kèm chính xác:

Web

onDisconnect(presenceRef).remove().catch((err) => {
  if (err) {
    console.error("could not establish onDisconnect event", err);
  }
});

Web

presenceRef.onDisconnect().remove((err) => {
  if (err) {
    console.error("could not establish onDisconnect event", err);
  }
});

Bạn cũng có thể huỷ một sự kiện onDisconnect bằng cách gọi .cancel():

Web

const onDisconnectRef = onDisconnect(presenceRef);
onDisconnectRef.set("I disconnected");
// some time later when we change our minds
onDisconnectRef.cancel();

Web

var onDisconnectRef = presenceRef.onDisconnect();
onDisconnectRef.set("I disconnected");
// some time later when we change our minds
onDisconnectRef.cancel();

Phát hiện trạng thái kết nối

Đối với nhiều tính năng liên quan đến sự hiện diện, tính năng này rất hữu ích cho ứng dụng của bạn biết khi nào thiết bị trực tuyến hoặc ngoại tuyến. Cơ sở dữ liệu theo thời gian thực của Firebase cung cấp một vị trí đặc biệt tại /.info/connected được cập nhật mỗi khi trạng thái kết nối của ứng dụng Cơ sở dữ liệu theo thời gian thực Firebase thay đổi. Bạn có thể tham khảo ví dụ sau đây:

Web

import { getDatabase, ref, onValue } from "firebase/database";

const db = getDatabase();
const connectedRef = ref(db, ".info/connected");
onValue(connectedRef, (snap) => {
  if (snap.val() === true) {
    console.log("connected");
  } else {
    console.log("not connected");
  }
});

Web

var connectedRef = firebase.database().ref(".info/connected");
connectedRef.on("value", (snap) => {
  if (snap.val() === true) {
    console.log("connected");
  } else {
    console.log("not connected");
  }
});

/.info/connected là một giá trị boolean không được đồng bộ hoá giữa các ứng dụng Cơ sở dữ liệu theo thời gian thực vì giá trị là phụ thuộc vào trạng thái của khách hàng. Nói cách khác, nếu một khách hàng đọc /.info/connected là false, giá trị này là không đảm bảo rằng một ứng dụng riêng biệt cũng sẽ đọc giá trị "false".

Độ trễ xử lý

Dấu thời gian của máy chủ

Máy chủ Cơ sở dữ liệu theo thời gian thực Firebase cung cấp cơ chế chèn dấu thời gian được tạo trên máy chủ dưới dạng dữ liệu. Tính năng này, kết hợp với onDisconnect, giúp bạn dễ dàng ghi chú một cách đáng tin cậy thời điểm ứng dụng Cơ sở dữ liệu theo thời gian thực ngắt kết nối:

Web

import { getDatabase, ref, onDisconnect, serverTimestamp } from "firebase/database";

const db = getDatabase();
const userLastOnlineRef = ref(db, "users/joe/lastOnline");
onDisconnect(userLastOnlineRef).set(serverTimestamp());

Web

var userLastOnlineRef = firebase.database().ref("users/joe/lastOnline");
userLastOnlineRef.onDisconnect().set(firebase.database.ServerValue.TIMESTAMP);

Mặt đồng hồ

Trong khi firebase.database.ServerValue.TIMESTAMP thì hiệu quả hơn nhiều chính xác và phù hợp nhất với hầu hết các thao tác đọc/ghi, Đôi khi, việc ước tính độ lệch đồng hồ của ứng dụng khách với so với máy chủ của Cơ sở dữ liệu theo thời gian thực Firebase. Bạn có thể đính kèm lệnh gọi lại vào vị trí /.info/serverTimeOffset để nhận được giá trị (tính bằng mili giây) mà ứng dụng Cơ sở dữ liệu thời gian thực Firebase thêm vào thời gian được báo cáo cục bộ (thời gian bắt đầu của hệ thống tính bằng mili giây) để ước tính theo thời gian máy chủ. Lưu ý rằng độ chính xác của giá trị bù trừ này có thể bị ảnh hưởng bởi độ trễ kết nối mạng, nên công cụ này chủ yếu hữu ích trong việc khám phá chênh lệch lớn (> 1 giây) về thời gian đồng hồ.

Web

import { getDatabase, ref, onValue } from "firebase/database";

const db = getDatabase();
const offsetRef = ref(db, ".info/serverTimeOffset");
onValue(offsetRef, (snap) => {
  const offset = snap.val();
  const estimatedServerTimeMs = new Date().getTime() + offset;
});

Web

var offsetRef = firebase.database().ref(".info/serverTimeOffset");
offsetRef.on("value", (snap) => {
  var offset = snap.val();
  var estimatedServerTimeMs = new Date().getTime() + offset;
});

Ứng dụng mẫu hiện diện

Bằng cách kết hợp các hoạt động ngắt kết nối với tính năng theo dõi trạng thái kết nối và dấu thời gian của máy chủ, bạn có thể xây dựng một hệ thống hiện diện người dùng. Trong hệ thống này, mỗi người dùng lưu trữ dữ liệu tại một vị trí cơ sở dữ liệu để cho biết liệu có Ứng dụng Cơ sở dữ liệu theo thời gian thực đang trực tuyến. Ứng dụng đặt vị trí này thành true khi khi các thiết bị này kết nối mạng và một dấu thời gian khi chúng ngắt kết nối. Dấu thời gian này cho biết lần cuối cùng người dùng cụ thể trực tuyến.

Lưu ý rằng ứng dụng phải đưa các hoạt động ngắt kết nối vào hàng đợi trước khi người dùng được đánh dấu là trực tuyến, để tránh mọi điều kiện tranh đấu trong trường hợp khách hàng bị mất kết nối mạng trước khi có thể gửi cả hai lệnh đến máy chủ.

Dưới đây là một hệ thống đơn giản giúp xác định sự hiện diện của người dùng:

Web

import { getDatabase, ref, onValue, push, onDisconnect, set, serverTimestamp } from "firebase/database";

// Since I can connect from multiple devices or browser tabs, we store each connection instance separately
// any time that connectionsRef's value is null (i.e. has no children) I am offline
const db = getDatabase();
const myConnectionsRef = ref(db, 'users/joe/connections');

// stores the timestamp of my last disconnect (the last time I was seen online)
const lastOnlineRef = ref(db, 'users/joe/lastOnline');

const connectedRef = ref(db, '.info/connected');
onValue(connectedRef, (snap) => {
  if (snap.val() === true) {
    // We're connected (or reconnected)! Do anything here that should happen only if online (or on reconnect)
    const con = push(myConnectionsRef);

    // When I disconnect, remove this device
    onDisconnect(con).remove();

    // Add this device to my connections list
    // this value could contain info about the device or a timestamp too
    set(con, true);

    // When I disconnect, update the last time I was seen online
    onDisconnect(lastOnlineRef).set(serverTimestamp());
  }
});

Web

// Since I can connect from multiple devices or browser tabs, we store each connection instance separately
// any time that connectionsRef's value is null (i.e. has no children) I am offline
var myConnectionsRef = firebase.database().ref('users/joe/connections');

// stores the timestamp of my last disconnect (the last time I was seen online)
var lastOnlineRef = firebase.database().ref('users/joe/lastOnline');

var connectedRef = firebase.database().ref('.info/connected');
connectedRef.on('value', (snap) => {
  if (snap.val() === true) {
    // We're connected (or reconnected)! Do anything here that should happen only if online (or on reconnect)
    var con = myConnectionsRef.push();

    // When I disconnect, remove this device
    con.onDisconnect().remove();

    // Add this device to my connections list
    // this value could contain info about the device or a timestamp too
    con.set(true);

    // When I disconnect, update the last time I was seen online
    lastOnlineRef.onDisconnect().set(firebase.database.ServerValue.TIMESTAMP);
  }
});