Abilitazione delle funzionalità offline in JavaScript

Mantieni tutto organizzato con le raccolte Salva e classifica i contenuti in base alle tue preferenze.

Le applicazioni Firebase funzionano anche se la tua app perde temporaneamente la connessione di rete. Forniamo diversi strumenti per il monitoraggio della presenza e la sincronizzazione dello stato locale con lo stato del server, che vengono introdotti in questo documento.

Gestire la presenza

Nelle applicazioni in tempo reale è spesso utile rilevare quando i client si connettono e si disconnettono. Ad esempio, potresti voler contrassegnare un utente come "non in linea" quando il suo client si disconnette.

I client Firebase Database forniscono semplici primitive che puoi utilizzare per scrivere nel database quando un client si disconnette dai server Firebase Database. Questi aggiornamenti si verificano indipendentemente dal fatto che il client si disconnette in modo pulito o meno, quindi puoi fare affidamento su di essi per ripulire i dati anche se una connessione viene interrotta o un client si arresta in modo anomalo. Tutte le operazioni di scrittura, incluse l'impostazione, l'aggiornamento e la rimozione, possono essere eseguite in caso di disconnessione.

Ecco un semplice esempio di scrittura dei dati al momento della disconnessione utilizzando la primitiva onDisconnect :

Web version 9

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 version 8

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

Come funziona onDisconnect

Quando stabilisci un'operazione onDisconnect() , l'operazione risiede sul server Firebase Realtime Database. Il server controlla la sicurezza per assicurarsi che l'utente possa eseguire l'evento di scrittura richiesto e informa l'app se non è valido. Il server monitora quindi la connessione. Se in qualsiasi momento la connessione va in timeout o viene chiusa attivamente dal client Realtime Database, il server controlla la sicurezza una seconda volta (per assicurarsi che l'operazione sia ancora valida) e quindi richiama l'evento.

La tua app può utilizzare il callback sull'operazione di scrittura per garantire che onDisconnect sia stato collegato correttamente:

Web version 9

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

Web version 8

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

Un evento onDisconnect può anche essere annullato chiamando .cancel() :

Web version 9

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

Web version 8

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

Rilevamento dello stato della connessione

Per molte funzionalità relative alla presenza, è utile che la tua app sappia quando è online o offline. Firebase Realtime Database fornisce una posizione speciale in /.info/connected che viene aggiornata ogni volta che cambia lo stato di connessione del client Firebase Realtime Database. Ecco un esempio:

Web version 9

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 version 8

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 è un valore booleano che non è sincronizzato tra i client Realtime Database poiché il valore dipende dallo stato del client. In altre parole, se un client legge /.info/connected come false, ciò non garantisce che anche un client separato legga false.

Gestione della latenza

Timestamp del server

I server Firebase Realtime Database forniscono un meccanismo per inserire i timestamp generati sul server come dati. Questa funzione, combinata con onDisconnect , fornisce un modo semplice per annotare in modo affidabile l'ora in cui un client Realtime Database si è disconnesso:

Web version 9

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

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

Web version 8

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

Inclinazione dell'orologio

Mentre firebase.database.ServerValue.TIMESTAMP è molto più accurato e preferibile per la maggior parte delle operazioni di lettura/scrittura, occasionalmente può essere utile stimare lo sfasamento dell'orologio del client rispetto ai server del Firebase Realtime Database. Puoi allegare una richiamata alla posizione /.info/serverTimeOffset per ottenere il valore, in millisecondi, che i client Firebase Realtime Database aggiungono all'ora locale segnalata (ora dell'epoca in millisecondi) per stimare l'ora del server. Si noti che la precisione di questo offset può essere influenzata dalla latenza di rete e pertanto è utile principalmente per rilevare discrepanze di grandi dimensioni (> 1 secondo) nell'ora dell'orologio.

Web version 9

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 version 8

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

App di presenza di esempio

Combinando le operazioni di disconnessione con il monitoraggio dello stato della connessione e i timestamp del server, è possibile creare un sistema di presenza dell'utente. In questo sistema, ogni utente memorizza i dati in una posizione del database per indicare se un client di Realtime Database è online o meno. I client impostano questa posizione su true quando sono online e un timestamp quando si disconnettono. Questo timestamp indica l'ultima volta che l'utente è stato online.

Tieni presente che la tua app deve mettere in coda le operazioni di disconnessione prima che un utente venga contrassegnato online, per evitare race condition nel caso in cui la connessione di rete del client venga persa prima che entrambi i comandi possano essere inviati al server.

Ecco un semplice sistema di presenza utente:

Web version 9

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 version 8

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