Activation des fonctionnalités hors ligne dans JavaScript

Les applications Firebase fonctionnent même si votre application perd temporairement sa connexion réseau. Nous fournissons plusieurs outils pour surveiller la présence et synchroniser l'état local avec l'état du serveur, qui sont présentés dans ce document.

Gérer la présence

Dans les applications en temps réel, il est souvent utile de détecter quand les clients se connectent et se déconnectent. Par exemple, vous pouvez marquer un utilisateur comme "hors ligne" lorsque son client se déconnecte.

Les clients Firebase Database fournissent des primitives simples que vous pouvez utiliser pour écrire dans la base de données lorsqu'un client se déconnecte des serveurs Firebase Database. Ces mises à jour se produisent que le client se déconnecte proprement ou non, vous pouvez donc compter sur elles pour nettoyer les données même si une connexion est interrompue ou si un client tombe en panne. Toutes les opérations d'écriture, y compris la définition, la mise à jour et la suppression, peuvent être effectuées lors d'une déconnexion.

Voici un exemple simple d'écriture de données lors de la déconnexion à l'aide de la primitive 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!");

Comment fonctionne la déconnexion

Lorsque vous établissez une opération onDisconnect() , l'opération réside sur le serveur Firebase Realtime Database. Le serveur vérifie la sécurité pour s'assurer que l'utilisateur peut effectuer l'événement d'écriture demandé et informe votre application s'il n'est pas valide. Le serveur surveille alors la connexion. Si, à un moment quelconque, la connexion expire ou est activement fermée par le client de la base de données en temps réel, le serveur vérifie la sécurité une deuxième fois (pour s'assurer que l'opération est toujours valide), puis invoque l'événement.

Votre application peut utiliser le rappel sur l'opération d'écriture pour s'assurer que l' onDisconnect a été correctement attaché :

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 événement onDisconnect peut également être annulé en appelant .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();

Détection de l'état de la connexion

Pour de nombreuses fonctionnalités liées à la présence, il est utile que votre application sache quand elle est en ligne ou hors ligne. Firebase Realtime Database fournit un emplacement spécial sur /.info/connected qui est mis à jour chaque fois que l'état de connexion du client Firebase Realtime Database change. Voici un exemple:

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 est une valeur booléenne qui n'est pas synchronisée entre les clients Realtime Database car la valeur dépend de l'état du client. En d'autres termes, si un client lit /.info/connected comme faux, cela ne garantit pas qu'un autre client lira également faux.

Gestion de la latence

Horodatages du serveur

Les serveurs Firebase Realtime Database fournissent un mécanisme pour insérer les horodatages générés sur le serveur en tant que données. Cette fonctionnalité, associée à onDisconnect , fournit un moyen simple de noter de manière fiable l'heure à laquelle un client Realtime Database s'est déconnecté :

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

Décalage de l'horloge

Bien que firebase.database.ServerValue.TIMESTAMP soit beaucoup plus précis et préférable pour la plupart des opérations de lecture/écriture, il peut parfois être utile d'estimer le décalage d'horloge du client par rapport aux serveurs de la base de données Firebase Realtime. Vous pouvez joindre un rappel à l'emplacement /.info/serverTimeOffset pour obtenir la valeur, en millisecondes, que les clients Firebase Realtime Database ajoutent à l'heure locale signalée (heure d'époque en millisecondes) pour estimer l'heure du serveur. Notez que la précision de ce décalage peut être affectée par la latence du réseau, et est donc principalement utile pour découvrir des écarts importants (> 1 seconde) dans l'heure de l'horloge.

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

Exemple d'application de présence

En combinant les opérations de déconnexion avec la surveillance de l'état de la connexion et les horodatages du serveur, vous pouvez créer un système de présence des utilisateurs. Dans ce système, chaque utilisateur stocke des données à un emplacement de base de données pour indiquer si un client de base de données en temps réel est en ligne ou non. Les clients définissent cet emplacement sur true lorsqu'ils se connectent et un horodatage lorsqu'ils se déconnectent. Cet horodatage indique la dernière fois que l'utilisateur donné était en ligne.

Notez que votre application doit mettre en file d'attente les opérations de déconnexion avant qu'un utilisateur ne soit marqué en ligne, afin d'éviter toute condition de concurrence dans le cas où la connexion réseau du client est perdue avant que les deux commandes puissent être envoyées au serveur.

Voici un système de présence utilisateur simple :

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