Aktivieren von Offline-Funktionen in JavaScript

Firebase-Anwendungen funktionieren auch dann, wenn die Netzwerkverbindung Ihrer App vorübergehend unterbrochen wird. Wir bieten verschiedene Tools zur Überwachung der Präsenz und zur Synchronisierung des lokalen Status mit dem Serverstatus, die in diesem Dokument vorgestellt werden.

Präsenz verwalten

In Echtzeitanwendungen ist es oft nützlich zu erkennen, wann sich Clients verbinden und trennen. Sie können beispielsweise einen Nutzer als „offline“ markieren, wenn die Verbindung des Clients getrennt wird.

Firebase Database-Clients bieten einfache Primitive, mit denen Sie in die Datenbank schreiben können, wenn die Verbindung eines Clients zu den Firebase Database-Servern getrennt wird. Diese Aktualisierungen erfolgen unabhängig davon, ob die Verbindung des Clients ordnungsgemäß getrennt wird oder nicht, Sie können sich also darauf verlassen, dass Daten bereinigt werden, auch wenn eine Verbindung unterbrochen wird oder ein Client abstürzt. Alle Schreibvorgänge, einschließlich Festlegen, Aktualisieren und Entfernen, können nach dem Trennen der Verbindung ausgeführt werden.

Hier ein einfaches Beispiel für das Schreiben von Daten nach dem Trennen der Verbindung mit dem onDisconnect Primitiv:

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!");

Funktionsweise von „onDisconnect“

Wenn Sie einen onDisconnect()-Vorgang einrichten, wird er auf dem Firebase Realtime Database-Server ausgeführt. Der Server prüft die Sicherheit, um sicherzustellen, dass der Nutzer das angeforderte Schreibereignis ausführen kann, und informiert Ihre App, wenn es ungültig ist. Anschließend überwacht der Server die Verbindung. Wenn die Verbindung irgendwann eine Zeitüberschreitung verursacht oder aktiv vom Realtime Database Client geschlossen wird, prüft der Server die Sicherheit ein zweites Mal (um sicherzustellen, dass der Vorgang noch gültig ist) und ruft dann das Ereignis auf.

Ihre App kann den Callback für den Schreibvorgang verwenden um sicherzustellen, dass der onDisconnect korrekt angehängt wurde:

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

Ein onDisconnect-Ereignis kann auch durch Aufrufen von .cancel() abgebrochen werden:

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();

Verbindungsstatus erkennen

Für viele präsenzbezogene Funktionen ist es nützlich, wenn Ihre App weiß, ob sie online oder offline ist. Firebase Realtime Database bietet einen speziellen Speicherort unter /.info/connected, der jedes Mal aktualisiert wird, wenn sich der Verbindungsstatus des Firebase Realtime Database-Clients ändert. Hier ein Beispiel:

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 ist ein boolescher Wert, der nicht zwischen Realtime Database Clients synchronisiert wird, da er vom Status des Clients abhängt. Wenn ein Client /.info/connected als „false“ liest, ist das also keine Garantie dafür, dass ein anderer Client ebenfalls „false“ liest.

Latenz verarbeiten

Server-Zeitstempel

Die Firebase Realtime Database Server bieten einen Mechanismus zum Einfügen Zeitstempel, die auf dem Server generiert wurden, als Daten. In Kombination mit onDisconnect, können Sie so auf einfache Weise zuverlässig den Zeitpunkt erfassen, zu dem die Verbindung eines Realtime Database Clients getrennt wurde:

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

Uhrzeitabweichung

`firebase.database.ServerValue.TIMESTAMP` ist zwar viel genauer und für die meisten Lese-/Schreibvorgänge vorzuziehen, aber gelegentlich kann es nützlich sein, die Uhrzeitabweichung des Clients in Bezug auf die firebase.database.ServerValue.TIMESTAMP zu schätzen.Firebase Realtime Database Sie können einen Callback an den Speicherort /.info/serverTimeOffset anhängen, um den Wert in Millisekunden abzurufen, den Firebase Realtime Database Clients zur lokal gemeldeten Zeit (Epochenzeit in Millisekunden) hinzufügen, um die Serverzeit zu schätzen. Die Genauigkeit dieses Offsets kann durch die Netzwerklatenz beeinträchtigt werden. Es ist daher hauptsächlich nützlich, um große (> 1 Sekunde) Abweichungen in der Uhrzeit zu erkennen.

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

Beispiel-App für die Präsenz

Durch die Kombination von Vorgängen zum Trennen der Verbindung mit der Überwachung des Verbindungsstatus und Server-Zeitstempeln können Sie ein System für die Nutzerpräsenz erstellen. In diesem System speichert jeder Nutzer Daten an einem Datenbankspeicherort, um anzugeben, ob ein Realtime Database Client online ist oder nicht. Clients legen diesen Speicherort auf „true“ fest, wenn sie online gehen, und auf einen Zeitstempel, wenn sie die Verbindung trennen. Dieser Zeitstempel gibt an, wann der jeweilige Nutzer zuletzt online war.

Ihre App sollte die Vorgänge zum Trennen der Verbindung in die Warteschlange stellen, bevor ein Nutzer als online markiert wird, um Race-Bedingungen zu vermeiden, falls die Netzwerkverbindung des Clients unterbrochen wird, bevor beide Befehle an den Server gesendet werden können.

Hier ein einfaches System für die Nutzerpräsenz:

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