Włączanie funkcji offline w JavaScripcie

Aplikacje Firebase działają nawet wtedy, gdy aplikacja tymczasowo utraci połączenie sieciowe. Udostępniamy kilka narzędzi do monitorowania obecności i synchronizowania stanu lokalnego ze stanem serwera, które opisujemy w tym dokumencie.

Zarządzanie obecnością

W aplikacjach działających w czasie rzeczywistym często przydaje się możliwość wykrywania, kiedy klienty łączą się i rozłączają. Możesz na przykład oznaczyć użytkownika jako użytkownika offline, gdy jego klient się rozłączy.

Klienty bazy danych Firebase udostępniają proste elementy podstawowe, których można używać do zapisywania w bazie danych, gdy klient odłączy się od serwerów bazy danych Firebase. Aktualizacje te są przeprowadzane niezależnie od tego, czy klient się rozłącza, czy też nie. Dzięki temu możesz mieć pewność, że dane zostaną wyczyszczone nawet w przypadku utraty połączenia lub awarii klienta. Wszystkie operacje zapisu, w tym ustawianie, aktualizowanie i usuwanie, mogą być wykonywane po odłączeniu.

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

Interfejs API internetowej przestrzeni nazw

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

Jak onOdłącz działa?

Gdy utworzysz operację onDisconnect(), będzie ona działać na serwerze Bazy danych czasu rzeczywistego Firebase. 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 zostanie przekroczone lub zostanie aktywnie zamknięte przez klienta Bazy danych czasu rzeczywistego, serwer jeszcze raz sprawdzi zabezpieczenia (aby upewnić się, że operacja jest nadal prawidłowa), a następnie wywoła zdarzenie.

Aplikacja może używać wywołania zwrotnego podczas zapisu, aby sprawdzić, czy onDisconnect został prawidłowo podłączony:

Web Modular API

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

Interfejs API internetowej przestrzeni nazw

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

Wydarzenie onDisconnect można też anulować, dzwoniąc pod numer .cancel():

Web Modular API

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

Interfejs API internetowej przestrzeni nazw

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

Wykrywam stan połączenia

W przypadku wielu funkcji związanych z obecnością aplikacja może wiedzieć, czy jest online czy offline. Baza danych czasu rzeczywistego Firebase udostępnia specjalną lokalizację pod adresem /.info/connected, która jest aktualizowana za każdym razem, gdy zmienia się stan połączenia klienta Bazy danych czasu rzeczywistego Firebase. Oto przykład:

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

Interfejs API internetowej przestrzeni nazw

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 Bazy danych czasu rzeczywistego, ponieważ zależy od stanu klienta. Inaczej mówiąc, jeśli jeden klient odczyta /.info/connected jako fałsz, nie ma gwarancji, że inny klient również odczyta ten parametr.

Czas oczekiwania na obsługę

Sygnatury czasowe serwera

Serwery Bazy danych czasu rzeczywistego Firebase udostępniają mechanizm wstawiania sygnatur czasowych wygenerowanych na serwerze jako dane. Ta funkcja w połączeniu z funkcją onDisconnect pozwala w łatwy sposób zanotować moment, w którym klient Bazy danych czasu rzeczywistego rozłączył się:

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

Interfejs API internetowej przestrzeni nazw

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

Zniekształcenie zegara

Chociaż metoda firebase.database.ServerValue.TIMESTAMP jest znacznie dokładniejsza i zalecana w przypadku większości operacji odczytu i zapisu, czasami może się przydać do oszacowania zniekształcenia zegara klienta w odniesieniu do serwerów Bazy danych czasu rzeczywistego Firebase. Możesz dołączyć wywołanie zwrotne do lokalizacji /.info/serverTimeOffset, aby uzyskać (w milisekundach) wartość, którą klienty Bazy danych czasu rzeczywistego Firebase dodają do lokalnego czasu zgłaszanego (w milisekundach) w celu oszacowania czasu 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 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;
});

Interfejs API internetowej przestrzeni nazw

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

Przykładowa aplikacja Obecność

Łącząc operacje rozłączania z monitorowaniem stanu połączenia i sygnaturami czasowymi serwera, możesz utworzyć system wykrywania obecności użytkowników. W tym systemie każdy użytkownik przechowuje dane w lokalizacji bazy danych, aby wskazać, czy klient Bazy danych czasu rzeczywistego jest online. Klienty ustawiają tę lokalizację na Prawda, gdy są 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żytkowników:

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

Interfejs API internetowej przestrzeni nazw

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