Mengaktifkan Kemampuan Offline di JavaScript

Aplikasi Firebase tetap berfungsi meskipun aplikasi Anda kehilangan koneksi jaringan untuk sementara. Kami menyediakan beberapa alat untuk memantau kehadiran dan menyinkronkan status lokal dengan status server, yang diperkenalkan dalam dokumen ini.

Mengelola Kehadiran

Pada aplikasi real-time, mendeteksi kapan klien membuat dan memutus koneksi sering kali bermanfaat. Misalnya, mungkin Anda ingin menandai pengguna sebagai 'offline' saat kliennya memutus koneksi.

Klien Firebase Database menyediakan sejumlah primitif sederhana yang dapat Anda gunakan untuk menulis ke database saat suatu klien memutuskan koneksi ke server Firebase Database. Update ini akan terjadi, terlepas dari apakah klien memutuskan koneksi secara bersih atau tidak, sehingga Anda dapat mengandalkan update ini untuk membersihkan data, meskipun koneksi terputus atau klien mengalami error. Semua operasi tulis, termasuk penyetelan, update, dan penghapusan, dapat dilakukan setelah koneksi terputus.

Berikut contoh sederhana penulisan data setelah koneksi diputus menggunakan primitif onDisconnect:

API modular 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!");

API dengan namespace web

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

Cara Kerja onDisconnect

Ketika Anda membuat operasi onDisconnect(), operasi tersebut akan berlangsung di server Firebase Realtime Database. Server akan memeriksa keamanan untuk memastikan bahwa pengguna dapat menjalankan peristiwa tulis yang diminta, dan memberi tahu aplikasi Anda jika operasi tersebut tidak valid. Selanjutnya, server akan memantau koneksinya. Jika waktu koneksi habis, atau koneksi ditutup secara aktif oleh klien Realtime Database, server akan memeriksa keamanan sekali lagi (untuk memastikan operasi masih valid), kemudian memanggil peristiwa tersebut.

Aplikasi Anda dapat menggunakan callback di operasi tulis untuk memastikan onDisconnect terpasang dengan benar:

API modular web

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

API dengan namespace web

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

Peristiwa onDisconnect juga dapat dibatalkan dengan memanggil .cancel():

API modular web

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

API dengan namespace web

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

Mendeteksi Status Koneksi

Untuk banyak fitur yang terkait dengan kehadiran, ada baiknya jika aplikasi mengetahui apakah status koneksi sedang online atau offline. Firebase Realtime Database menyediakan lokasi khusus di /.info/connected yang diperbarui setiap kali status koneksi klien Firebase Realtime Database berubah. Berikut ini contohnya:

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

API dengan namespace 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 adalah nilai boolean yang tidak disinkronkan antarklien Realtime Database karena nilainya bergantung pada status klien. Dengan kata lain, jika satu klien membaca /.info/connected sebagai 'false', tidak ada jaminan bahwa klien lain juga akan membacanya sebagai 'false'.

Menangani Latensi

Stempel Waktu Server

Server Firebase Realtime Database memberikan mekanisme untuk menyisipkan stempel waktu yang dibuat di server sebagai data. Fitur ini, bersama dengan onDisconnect, memberikan cara mudah dan andal untuk mencatat kapan koneksi klien Realtime Database diputus:

API modular web

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

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

API dengan namespace web

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

Clock Skew

Meskipun firebase.database.ServerValue.TIMESTAMP jauh lebih akurat dan lebih cocok untuk sebagian besar operasi baca/tulis, terkadang ada baiknya memperkirakan clock skew klien terhadap server Firebase Realtime Database. Anda dapat menambahkan callback ke lokasi /.info/serverTimeOffset untuk mendapatkan nilai, dalam milidetik, yang ditambahkan oleh klien Firebase Realtime Database ke waktu lokal yang dilaporkan (waktu epoch dalam milidetik) untuk memperkirakan waktu server. Perlu diperhatikan bahwa akurasi selisih ini dapat dipengaruhi oleh latensi jaringan, sehingga hanya bermanfaat untuk menemukan selisih waktu jam yang besar (> 1 detik).

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

API dengan namespace web

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

Contoh Aplikasi Kehadiran

Dengan menggabungkan operasi pemutusan koneksi dengan pemantauan status koneksi dan stempel waktu server, Anda dapat membuat suatu sistem kehadiran pengguna. Dalam sistem ini, setiap pengguna menyimpan data di suatu lokasi database untuk menunjukkan apakah klien Realtime Database sedang online atau tidak. Klien akan menetapkan lokasi ini ke benar (true) ketika mulai online dan ke stempel waktu ketika memutuskan koneksi. Stempel waktu ini menunjukkan kapan terakhir kali pengguna tersebut online.

Perlu diperhatikan bahwa aplikasi Anda harus mengantrekan operasi pemutusan koneksi sebelum pengguna ditandai sebagai online, untuk menghindari kondisi race yang dapat terjadi jika koneksi jaringan klien terputus sebelum kedua perintah dapat dikirimkan ke server.

Berikut adalah contoh sederhana sistem kehadiran pengguna:

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

API dengan namespace 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);
  }
});