Catch up on everything announced at Firebase Summit, and learn how Firebase can help you accelerate app development and run your app with confidence. Learn More

Habilitando recursos offline em JavaScript

Mantenha tudo organizado com as coleções Salve e categorize o conteúdo com base nas suas preferências.

Os aplicativos Firebase funcionam mesmo se seu aplicativo perder a conexão de rede temporariamente. Fornecemos várias ferramentas para monitorar a presença e sincronizar o estado local com o estado do servidor, que são apresentadas neste documento.

Gerenciando Presença

Em aplicações de tempo real, muitas vezes é útil detectar quando os clientes se conectam e desconectam. Por exemplo, você pode querer marcar um usuário como 'offline' quando seu cliente se desconectar.

Os clientes do Firebase Database fornecem primitivas simples que você pode usar para gravar no banco de dados quando um cliente se desconecta dos servidores do Firebase Database. Essas atualizações ocorrem independentemente de o cliente se desconectar corretamente ou não, portanto, você pode confiar nelas para limpar os dados mesmo se uma conexão cair ou um cliente travar. Todas as operações de gravação, incluindo configuração, atualização e remoção, podem ser executadas após uma desconexão.

Aqui está um exemplo simples de gravação de dados após a desconexão usando a 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!");

Como funciona o onDisconnect

Quando você estabelece uma operação onDisconnect() , a operação reside no servidor Firebase Realtime Database. O servidor verifica a segurança para garantir que o usuário possa executar o evento de gravação solicitado e informa ao seu aplicativo se ele for inválido. O servidor então monitora a conexão. Se a qualquer momento a conexão atingir o tempo limite ou for fechada ativamente pelo cliente do Realtime Database, o servidor verificará a segurança uma segunda vez (para garantir que a operação ainda seja válida) e então invocará o evento.

Seu aplicativo pode usar o retorno de chamada na operação de gravação para garantir que o onDisconnect foi anexado corretamente:

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

Um evento onDisconnect também pode ser cancelado chamando .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();

Detectando o estado da conexão

Para muitos recursos relacionados à presença, é útil que seu aplicativo saiba quando está online ou offline. O Firebase Realtime Database fornece um local especial em /.info/connected , que é atualizado sempre que o estado da conexão do cliente do Firebase Realtime Database muda. Aqui está um exemplo:

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 é um valor booleano que não é sincronizado entre os clientes do Realtime Database porque o valor depende do estado do cliente. Em outras palavras, se um cliente lê /.info/connected como falso, isso não garante que outro cliente também leia falso.

Lidando com a latência

Carimbos de data/hora do servidor

Os servidores Firebase Realtime Database fornecem um mecanismo para inserir timestamps gerados no servidor como dados. Esse recurso, combinado com onDisconnect , fornece uma maneira fácil de anotar com segurança a hora em que um cliente do Realtime Database foi desconectado:

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

Inclinação do relógio

Embora firebase.database.ServerValue.TIMESTAMP seja muito mais preciso e preferível para a maioria das operações de leitura/gravação, ocasionalmente pode ser útil estimar a inclinação do relógio do cliente em relação aos servidores do Firebase Realtime Database. Você pode anexar um retorno de chamada ao local /.info/serverTimeOffset para obter o valor, em milissegundos, que os clientes do Firebase Realtime Database adicionam ao horário local relatado (hora da época em milissegundos) para estimar a hora do servidor. Observe que a precisão desse deslocamento pode ser afetada pela latência da rede e, portanto, é útil principalmente para descobrir grandes discrepâncias (> 1 segundo) no horário do relógio.

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

Exemplo de aplicativo de presença

Combinando operações de desconexão com monitoramento do estado da conexão e carimbos de data/hora do servidor, você pode criar um sistema de presença do usuário. Nesse sistema, cada usuário armazena dados em um local de banco de dados para indicar se um cliente do Realtime Database está online ou não. Os clientes definem esse local como verdadeiro quando ficam online e um carimbo de data/hora quando se desconectam. Esse carimbo de data/hora indica a última vez que o usuário em questão esteve online.

Observe que seu aplicativo deve enfileirar as operações de desconexão antes que um usuário seja marcado como online, para evitar quaisquer condições de corrida caso a conexão de rede do cliente seja perdida antes que ambos os comandos possam ser enviados ao servidor.

Aqui está um sistema simples de presença do usuário:

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