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

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

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

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

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

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

واجهة برمجة التطبيقات Web modular API

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

واجهة برمجة التطبيقات لمساحة الاسم على الويب

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

طريقة عمل ميزة on Connect

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

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

واجهة برمجة التطبيقات Web modular API

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

واجهة برمجة التطبيقات لمساحة الاسم على الويب

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

يمكن أيضًا إلغاء حدث "onDisconnect" من خلال الاتصال بالرقم .cancel():

واجهة برمجة التطبيقات Web modular API

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

واجهة برمجة التطبيقات لمساحة الاسم على الويب

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

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

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

واجهة برمجة التطبيقات Web modular API

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

واجهة برمجة التطبيقات لمساحة الاسم على الويب

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

وقت الاستجابة

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

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

واجهة برمجة التطبيقات Web modular API

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

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

واجهة برمجة التطبيقات لمساحة الاسم على الويب

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

انحراف الساعة

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

واجهة برمجة التطبيقات Web modular API

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

واجهة برمجة التطبيقات لمساحة الاسم على الويب

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

نموذج تطبيق التواجد

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

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

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

واجهة برمجة التطبيقات Web modular API

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

واجهة برمجة التطبيقات لمساحة الاسم على الويب

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