Bật khả năng ngoại tuyến trong JavaScript

Các ứ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ụ để theo dõi sự hiện diện và đồng bộ hóa trạng thái cục bộ với trạng thái máy chủ, đượ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, việc phát hiện khi máy khách kết nối và ngắt kết nối thường rất hữu ích. Ví dụ: bạn có thể muốn đánh dấu người dùng là 'ngoại tuyến' khi ứng dụng khách của họ ngắt kết nối.

Máy khách Cơ sở dữ liệu Firebase cung cấp các nguyên hàm đơn giản mà bạn có thể sử dụng để ghi vào cơ sở dữ liệu khi máy khách ngắt kết nối khỏi máy chủ Cơ sở dữ liệu Firebase. Những cập nhật này xảy ra cho dù máy khách có ngắt kết nối hoàn toàn hay không, vì vậy bạn có thể dựa vào chúng để dọn sạch dữ liệu ngay cả khi kết nối bị ngắt hoặc máy khách gặp sự cố. Tất cả các thao tác ghi, bao gồm cài đặt, cập nhật và xóa, có thể được thực hiện khi ngắt kết nối.

Đây là một ví dụ đơn giản về việc ghi dữ liệu khi ngắt kết nối bằng cách sử dụng nguyên hàm onDisconnect :

Web modular API

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 namespaced API

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

Cách thức hoạt động của Ngắt kết nối

Khi bạn thiết lập một thao tác onDisconnect() , thao tác này sẽ tồn tại trên máy chủ Cơ sở dữ liệu 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 được yêu cầu và thông báo cho ứng dụng của bạn nếu nó không hợp lệ. Máy chủ sau đó 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 hoặc bị máy khách Cơ sở dữ liệu thời gian thực chủ động đóng, máy chủ sẽ kiểm tra bảo mật lần thứ hai (để đảm bảo hoạt động vẫn hợp lệ) rồi gọi sự kiện.

Ứng dụng của bạn có thể sử 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 modular API

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

Web namespaced API

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

Một sự kiện onDisconnect cũng có thể bị hủy bằng cách gọi .cancel() :

Web modular API

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

Web namespaced API

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

Đang 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, ứng dụng của bạn sẽ hữu ích khi biết khi nào ứng dụng trực tuyến hoặc ngoại tuyến. Cơ sở dữ liệu thời gian thực 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 máy khách Cơ sở dữ liệu thời gian thực Firebase thay đổi. Đây là một ví dụ:

Web modular API

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 namespaced API

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ộ hóa giữa các máy khách Cơ sở dữ liệu thời gian thực vì giá trị này phụ thuộc vào trạng thái của máy khách. Nói cách khác, nếu một khách hàng đọc /.info/connected là sai, điều này không đảm bảo rằng một khách hàng riêng biệt cũng sẽ đọc sai.

Xử lý độ trễ

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

Máy chủ Cơ sở dữ liệu 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, được kết hợp với onDisconnect , cung cấp một cách dễ dàng để ghi lại thời gian mà máy khách Cơ sở dữ liệu thời gian thực bị ngắt kết nối:

Web modular API

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

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

Web namespaced API

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

Đồng hồ nghiêng

Mặc dù firebase.database.ServerValue.TIMESTAMP chính xác hơn nhiều và thích hợp hơn cho hầu hết các hoạt động đọc/ghi, đôi khi có thể hữu ích khi ước tính độ lệch đồng hồ của máy khách so với máy chủ của Cơ sở dữ liệu 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 giá trị tính bằng mili giây mà ứng dụng khách 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 epoch tính bằng mili giây) để ước tính thời gian của máy chủ. Lưu ý rằng độ chính xác của phần bù này có thể bị ảnh hưởng bởi độ trễ của mạng và do đó, tính năng này chủ yếu hữu ích để khám phá các chênh lệch lớn (> 1 giây) trong thời gian đồng hồ.

Web modular API

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 namespaced API

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

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

Bằng cách kết hợp các hoạt động ngắt kết nối với giám sát 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 ứng dụng khách Cơ sở dữ liệu thời gian thực có trực tuyến hay không. Khách hàng đặt vị trí này thành true khi họ trực tuyến và dấu thời gian khi họ 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 của bạn nên xếp hàng đợi các hoạt động ngắt kết nối trước khi người dùng được đánh dấu trực tuyến, để tránh bất kỳ điều kiện tranh đua nào trong trường hợp kết nối mạng của máy khách bị mất trước khi cả hai lệnh có thể được gửi đến máy chủ.

Đây là một hệ thống hiện diện người dùng đơn giản:

Web modular API

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 namespaced API

// 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);
  }
});