تمكين إمكانات وضع عدم الاتصال في JavaScript

تعمل تطبيقات Firebase حتى إذا فقد تطبيقك اتصال الشبكة مؤقتًا. نحن نقدم العديد من الأدوات لمراقبة التواجد ومزامنة الحالة المحلية مع حالة الخادم ، والتي تم تقديمها في هذا المستند.

إدارة التواجد

غالبًا ما يكون من المفيد في تطبيقات الوقت الفعلي اكتشاف وقت اتصال العملاء وفصلهم. على سبيل المثال ، قد ترغب في وضع علامة "غير متصل" على المستخدم عند قطع اتصال العميل.

يوفر عملاء قاعدة بيانات Firebase عناصر أولية بسيطة يمكنك استخدامها للكتابة إلى قاعدة البيانات عند قطع اتصال العميل بخوادم قاعدة بيانات Firebase. تحدث هذه التحديثات سواء قام العميل بقطع الاتصال بشكل نظيف أم لا ، لذلك يمكنك الاعتماد عليها لتنظيف البيانات حتى في حالة انقطاع الاتصال أو تعطل العميل. يمكن إجراء جميع عمليات الكتابة ، بما في ذلك الإعداد والتحديث والإزالة ، عند قطع الاتصال.

فيما يلي مثال بسيط لكتابة البيانات عند قطع الاتصال باستخدام بدائية 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!");

كيف يعمل قطع الاتصال

عند إنشاء عملية onDisconnect() ، تستمر العملية على خادم قاعدة بيانات Firebase Realtime. يتحقق الخادم من الأمان للتأكد من أن المستخدم يمكنه تنفيذ حدث الكتابة المطلوب ، ويبلغ التطبيق إذا كان غير صالح. ثم يراقب الخادم الاتصال. إذا انتهت مهلة الاتصال في أي وقت ، أو تم إغلاقه بشكل نشط بواسطة عميل Realtime Database ، يتحقق الخادم من الأمان مرة ثانية (للتأكد من أن العملية لا تزال صالحة) ثم يستدعي الحدث.

يمكن لتطبيقك استخدام رد الاتصال في عملية الكتابة للتأكد من onDisconnect بشكل صحيح:

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

يمكن أيضًا إلغاء حدث onDisconnect عن طريق استدعاء .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();

الكشف عن حالة الاتصال

بالنسبة للعديد من الميزات المتعلقة بالحضور ، من المفيد أن يعرف تطبيقك متى يكون متصلاً أو غير متصل بالإنترنت. توفر قاعدة بيانات Firebase Realtime موقعًا خاصًا على /.info/connected والذي يتم تحديثه في كل مرة تتغير فيها حالة اتصال عميل قاعدة بيانات Firebase Realtime. هنا مثال:

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 هي قيمة منطقية لا تتم مزامنتها بين عملاء Realtime Database لأن القيمة تعتمد على حالة العميل. بمعنى آخر ، إذا قرأ أحد العملاء /.info/connected على أنه خطأ ، فهذا لا يضمن أن العميل المنفصل سيقرأ أيضًا خطأ.

التعامل مع الكمون

الطوابع الزمنية للخادم

توفر خوادم قاعدة بيانات Firebase Realtime آلية لإدراج الطوابع الزمنية التي تم إنشاؤها على الخادم كبيانات. توفر هذه الميزة ، جنبًا إلى جنب مع onDisconnect ، طريقة سهلة لتدوين الوقت الذي تم فيه فصل عميل Realtime Database بشكل موثوق:

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

انحراف ساعة

في حين أن firebase.database.ServerValue.TIMESTAMP أكثر دقة ومفضلًا لمعظم عمليات القراءة / الكتابة ، فقد يكون من المفيد أحيانًا تقدير انحراف ساعة العميل فيما يتعلق بخوادم قاعدة بيانات Firebase Realtime. يمكنك إرفاق رد اتصال بالموقع /.info/serverTimeOffset للحصول على القيمة ، بالمللي ثانية ، التي يضيفها عملاء قاعدة بيانات Firebase Realtime إلى الوقت المحلي المبلغ عنه (وقت الحقبة بالمللي ثانية) لتقدير وقت الخادم. لاحظ أن دقة هذا الإزاحة يمكن أن تتأثر بزمن انتقال الشبكة ، ولذا فهي مفيدة بشكل أساسي لاكتشاف تباينات كبيرة (> ثانية واحدة) في وقت الساعة.

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

نموذج تطبيق الحضور

من خلال الجمع بين عمليات قطع الاتصال ومراقبة حالة الاتصال والطوابع الزمنية للخادم ، يمكنك بناء نظام حضور المستخدم. في هذا النظام ، يقوم كل مستخدم بتخزين البيانات في موقع قاعدة البيانات للإشارة إلى ما إذا كان عميل Realtime Database متصل بالإنترنت أم لا. يقوم العملاء بتعيين هذا الموقع على "صحيح" عند الاتصال بالإنترنت والطابع الزمني عند قطع الاتصال. يشير هذا الطابع الزمني إلى آخر مرة كان فيها المستخدم متصلاً بالإنترنت.

لاحظ أن التطبيق الخاص بك يجب أن يضع عمليات قطع الاتصال في قائمة الانتظار قبل أن يتم وضع علامة على المستخدم على الإنترنت ، لتجنب أي ظروف سباق في حالة فقد اتصال شبكة العميل قبل إرسال كلا الأمرين إلى الخادم.

فيما يلي نظام بسيط لتواجد المستخدم:

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