Aktivieren von Offline-Funktionen in JavaScript

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

Präsenz verwalten

In Echtzeitanwendungen ist es oft nützlich zu erkennen, wenn Clients eine Verbindung herstellen und trennen. Sie möchten beispielsweise einen Benutzer als 'offline' markieren, wenn sein Client die Verbindung trennt.

Firebase Database-Clients bieten einfache Primitive, mit denen Sie in die Datenbank schreiben können, wenn ein Client die Verbindung zu den Firebase Database-Servern trennt. Diese Aktualisierungen erfolgen unabhängig davon, ob der Client die Verbindung sauber trennt oder nicht. Sie können sich also darauf verlassen, dass sie Daten bereinigen, selbst wenn eine Verbindung unterbrochen wird oder ein Client abstürzt. Alle Schreibvorgänge, einschließlich des Setzens, Aktualisierens und Entfernens, können bei einer Trennung ausgeführt werden.

Hier ist ein einfaches Beispiel für das Schreiben von Daten beim Trennen mit dem onDisconnect Primitiven:

Web v8

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

Web v9

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

Wie onDisconnect funktioniert

Wenn Sie einen onDisconnect() Vorgang onDisconnect() , befindet sich der Vorgang auf dem Firebase Realtime Database-Server. Der Server überprüft die Sicherheit, um sicherzustellen, dass der Benutzer das angeforderte Schreibereignis ausführen kann, und informiert Ihre App, wenn es ungültig ist. Der Server überwacht dann die Verbindung. Wenn die Verbindung zu irgendeinem Zeitpunkt abläuft oder vom Realtime Database-Client aktiv geschlossen wird, überprü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 Rückruf beim Schreibvorgang verwenden, um sicherzustellen, dass onDisconnect korrekt angehängt wurde:

Web v8

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

Web v9

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

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

Web v8

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

Web v9

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

Verbindungsstatus erkennen

Bei vielen präsenzbezogenen Funktionen ist es hilfreich, 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 ist ein Beispiel:

Web v8

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

Web v9

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

/.info/connected ist ein boolescher Wert, der nicht zwischen Realtime Database-Clients synchronisiert wird, da der Wert vom Status des Clients abhängt. Mit anderen Worten, wenn ein Client /.info/connected als false liest, ist dies keine Garantie dafür, dass ein separater Client auch false liest.

Umgang mit Latenz

Server-Zeitstempel

Die Firebase Realtime Database-Server bieten einen Mechanismus zum Einfügen von auf dem Server generierten Zeitstempeln als Daten. Diese Funktion bietet in Kombination mit onDisconnect eine einfache Möglichkeit, zuverlässig die Zeit zu notieren, zu der ein Echtzeitdatenbank-Client die Verbindung getrennt hat:

Web v8

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

Web v9

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

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

Uhrversatz

Obwohl firebase.database.ServerValue.TIMESTAMP viel genauer ist und für die meisten Lese-/Schreibvorgänge vorzuziehen ist, kann es gelegentlich nützlich sein, die Taktabweichung des Clients in Bezug auf die Server der Firebase Realtime Database zu schätzen. Sie können einen Rückruf an den Speicherort /.info/serverTimeOffset , um den Wert in Millisekunden /.info/serverTimeOffset , den Firebase Realtime Database-Clients zur lokalen gemeldeten Zeit (Epochenzeit in Millisekunden) addieren, um die Serverzeit zu schätzen. Beachten Sie, dass die Genauigkeit dieses Offsets durch die Netzwerklatenz beeinflusst werden kann und daher in erster Linie nützlich ist, um große (> 1 Sekunde) Diskrepanzen in der Uhrzeit zu erkennen.

Web v8

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

Web v9

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

Beispiel-Präsenz-App

Durch die Kombination von Trennvorgängen mit Verbindungsstatusüberwachung und Serverzeitstempeln können Sie ein Benutzeranwesenheitssystem aufbauen. In diesem System speichert jeder Benutzer Daten an einem Datenbankspeicherort, um anzuzeigen, ob ein Echtzeitdatenbank-Client online ist oder nicht. Clients setzen diesen Speicherort auf true, wenn sie online gehen, und einen Zeitstempel, wenn sie die Verbindung trennen. Dieser Zeitstempel gibt an, wann der angegebene Benutzer das letzte Mal online war.

Beachten Sie, dass Ihre App die Trennvorgänge in die Warteschlange stellen sollte, bevor ein Benutzer 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 ist ein einfaches Benutzeranwesenheitssystem:

Web v8

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

Web v9

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