Włączanie funkcji offline w JavaScripcie

Aplikacje Firebase działają nawet wtedy, gdy aplikacja tymczasowo utraci połączenie z siecią. Udostępniamy kilka narzędzi do monitorowania obecności i synchronizowania stanu lokalnego ze stanem serwera. Są one opisane w tym dokumencie.

Zarządzanie obecnością

W przypadku aplikacji działających w czasie rzeczywistym często przydaje się wykrywanie momentów, w których klienci łączą się z serwerem i z niego rozłączają. Możesz na przykład oznaczyć użytkownika jako „offline”, gdy jego klient się rozłączy.

Klienty bazy danych Firebase udostępniają proste podstawowe elementy, których można używać do zapisywania w bazie danych, gdy klient odłączy się od serwerów bazy danych Firebase. Te aktualizacje mają miejsce niezależnie od tego, czy klient odłączy się prawidłowo, czy nie. Dzięki temu możesz polegać na tym, że dane zostaną oczyszczone nawet wtedy, gdy połączenie zostanie utracone lub klient ulegnie awarii. Po odłączeniu można wykonywać wszystkie operacje zapisu, w tym ustawianie, aktualizowanie i usuwanie.

Oto prosty przykład zapisywania danych po odłączeniu przy użyciu elementu podstawowego 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!");

Jak działa onDisconnect

Gdy utworzysz operację onDisconnect(), będzie ona działać na serwerze Firebase Realtime Database. Serwer sprawdza zabezpieczenia, by upewnić się, że użytkownik może wykonać żądane zdarzenie zapisu, i informuje aplikację, jeśli to zdarzenie jest nieprawidłowe. Serwer będzie monitorować połączenie. Jeśli w którymkolwiek momencie połączenie przekroczy limit czasu lub zostanie aktywnie zamknięte przez klienta Realtime Database, serwer ponownie sprawdzi zabezpieczenia (aby upewnić się, że operacja jest nadal prawidłowa), a następnie wywoła zdarzenie.

Aplikacja może użyć wywołania zwrotnego operacji zapisu, aby upewnić się, że onDisconnect został prawidłowo dołączony:

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

Wydarzenie onDisconnect można też anulować, wywołując funkcję .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();

Wykrywanie stanu połączenia

W przypadku wielu funkcji związanych z obecnością przydatne jest, aby aplikacja wiedziała, kiedy jest online, a kiedy offline. Firebase Realtime Database udostępnia specjalną lokalizację w adresie /.info/connected, która jest aktualizowana za każdym razem, gdy zmienia się stan połączenia klienta Firebase Realtime Database. Oto przykład:

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 to wartość logiczna, która nie jest synchronizowana między klientami Realtime Database, ponieważ zależy od stanu klienta. Innymi słowy, jeśli jeden klient odczytuje wartość /.info/connected jako fałsz, nie ma gwarancji, że inny klient również odczyta wartość jako fałsz.

Czas obsługi

Sygnatury czasowe serwera

Serwery Firebase Realtime Database udostępniają mechanizm wstawiania sygnatur czasowych wygenerowanych na serwerze jako danych. Ta funkcja w połączeniu z funkcją onDisconnect umożliwia łatwe i niezawodne zapisywanie informacji o czasie odłączenia klienta Realtime Database:

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

Zniekształcenie zegara

Funkcja firebase.database.ServerValue.TIMESTAMP jest znacznie dokładniejsza i preferowana w przypadku większości operacji odczytu/zapisu, ale czasami może być przydatna do oszacowania odchylenia zegara klienta względem serwerów Firebase Realtime Database. Do lokalizacji /.info/serverTimeOffset możesz dołączyć funkcję wywołania zwrotnego, aby uzyskać wartość w milisekundach, którą klienci Firebase Realtime Database dodają do lokalnego czasu zgłoszonego (czas epoki w milisekundach), aby oszacować czas serwera. Pamiętaj, że opóźnienie sieciowe może mieć wpływ na dokładność przesunięcia, dlatego jest ono przydatne głównie przy wykrywaniu dużych (powyżej 1 sekundy) rozbieżności w czasie zegara.

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

Przykładowa aplikacja do obsługi stanu obecności

Łącząc operacje rozłączania z monitorowaniem stanu połączenia i datą i godzinę serwera, możesz tworzyć systemy obecności użytkowników. W tym systemie każdy użytkownik przechowuje dane w lokalizacji bazy danych, aby wskazać, czy klient Realtime Database jest online. Klienty ustawiają tę lokalizację na „prawda”, gdy przechodzą do trybu online, oraz sygnaturę czasową po rozłączeniu. Ta sygnatura czasowa wskazuje, kiedy dany użytkownik ostatnio był online.

Pamiętaj, że aplikacja powinna kolejkować operacje odłączania, zanim użytkownik zostanie oznaczony jako online. Pozwoli to uniknąć wyścigów w przypadku utraty połączenia sieciowego klienta przed wysłaniem obu poleceń na serwer.

Oto prosty system wykrywania obecności użytkownika:

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