فحص حالة الاتصال بالإنترنت في تطبيقات الويب: حلول متقدمة باستخدام جافاسكريبت غير المتزامن

دقائق القراءة: 7
هل تساءلت يوماً عن إمكانية التحقق من اتصال تطبيق الويب الخاص بك بالإنترنت باستخدام جافاسكريبت؟ في هذا المقال، نستعرض إجابة حديثة ومفصلة لهذا التساؤل المحوري. سنقدم حلاً فعالاً يعتمد على واجهة برمجة تطبيقات Fetch API في جافاسكريبت، بالإضافة إلى الاستفادة من البرمجة غير المتزامنة باستخدام Async و Await. ولكن قبل الغوص في الحلول المتقدمة، دعنا نلقي نظرة على طريقة شائعة الاستخدام ونناقش لماذا قد لا تكون الخيار الأمثل لتطبيقك.

navigator.onLine: الحل الشائع ومحدودياته

تُعد خاصية online ضمن واجهة navigator، والتي يمكن الوصول إليها عبر navigator.onLine، من الطرق الشائعة للكشف عن حالة اتصال المتصفح بالإنترنت (متصل أو غير متصل). عند دمجها مع مستمعي الأحداث (event listeners) لحدثي online و offline، تبدو هذه الخاصية حلاً بسيطاً وسهل التنفيذ للمطورين. دعنا نستعرض كيفية تطبيق navigator.onLine.

تطبيق navigator.onLine الأساسي

للبدء، سنضيف مستمع حدث load. عندما يتم إطلاق حدث load، سيتحقق المستمع من خاصية online لواجهة navigator ثم يعرض حالة الاتصال. توفر خاصية online قيمة منطقية (boolean) إما true أو false. لإكمال الإجراء الخاص بالمستمع، سنستخدم عبارة شرطية ثلاثية (ternary statement) لتعيين قيمة عرض الحالة.


window.addEventListener( "load" , ( event ) => {
  const statusDisplay = document.getElementById( "status" );
  statusDisplay.textContent = navigator.onLine ? "Online" : "OFFline" ;
});

لماذا كلمة navigator تحديداً؟ حسناً، إنها إشارة إلى متصفح Netscape Navigator من التسعينيات. قم بوضع عنصر h1 في صفحة HTML الخاصة بك مع المعرف (id) “status” في المنتصف. إذا قمت بتطبيق كود جافاسكريبت أعلاه على صفحتك، يجب أن تراه يعرض “Online“. لكن هذا لا يحدث إلا عند تحميل الصفحة. دعنا نضيف مستمعي أحداث offline و online لتحديث عرض الحالة في أي وقت يتم فيه إطلاق أحد هذه الأحداث.


window.addEventListener( "offline" , ( event ) => {
  const statusDisplay = document.getElementById( "status" );
  statusDisplay.textContent = "OFFline" ;
});

window.addEventListener( "online" , ( event ) => {
  const statusDisplay = document.getElementById( "status" );
  statusDisplay.textContent = "Online" ;
});

يمكننا الانتقال إلى علامة التبويب Application في أدوات مطوري Chrome Dev Tools والنقر على ServiceWorker لتعيين المتصفح للاستجابة كما لو كان غير متصل بالإنترنت. قم بتحديد وإلغاء تحديد مربع Offline عدة مرات. يجب أن ترى عرض الحالة يستجيب على الفور لأحداث offline و online التي يتم إطلاقها.
لقطة شاشة لأدوات مطوري Chrome تظهر خيار Service Workers ومربع Offline المحدد لاختبار حالة الاتصال.

دعنا نتعمق أكثر: لماذا navigator.onLine قد لا يكون كافياً؟

للوهلة الأولى، قد يبدو الحل المذكور أعلاه جيداً وبسيطاً للغاية. لكن للأسف، عند التعمق في فهم خاصية online لواجهة navigator وأحداث online و offline، نكتشف وجود مشكلة جوهرية.

محدودية navigator.onLine: اتصال بالشبكة لا بالإنترنت

البحث عن navigator.onLine على موقع CanIUse.com يظهر دعماً واسع النطاق لحالة الاتصال/عدم الاتصال التي توفرها هذه الخاصية. ومع ذلك، بالنظر إلى الملاحظات أسفل جدول الدعم، نجد أن “Online لا يعني دائماً الاتصال بالإنترنت. يمكن أن يعني أيضاً مجرد الاتصال بشبكة ما”. هذا يثير بعض التساؤلات حول دقة هذه الخاصية. لذا، إذا كنت ترغب حقاً في تحديد حالة اتصال المتصفح بالإنترنت، يجب عليك تطوير وسائل إضافية للتحقق.

دعنا نلقي نظرة أيضاً على مرجع وثائق MDN web docs لـ navigator.onLine. تؤكد وثائق MDN المعلومات الواردة من CanIUse.com وتضيف ملاحظات إضافية: “تطبق المتصفحات هذه الخاصية بشكل مختلف… لا يمكنك افتراض أن القيمة true تعني بالضرورة أن المتصفح يمكنه الوصول إلى الإنترنت. قد تحصل على إيجابيات كاذبة…”.

هذا يؤكد مخاوفنا بشأن استخدام خاصية online لواجهة navigator كحل لتحديد اتصال الإنترنت. إنه حل قد يتسبب في مشاكل كبيرة لتطبيقاتنا التي تعتمد على معرفة متى تكون مصادر البيانات الخارجية متاحة. أحد الأمثلة على ذلك هو عندما نحاول تحديد ما إذا كان تطبيق ويب تقدمي (Progressive Web App - PWA) متصلاً بالإنترنت أم لا. حتى MDN توصي بأنه “…إذا كنت تريد حقاً تحديد حالة اتصال المتصفح بالإنترنت، يجب عليك تطوير وسائل إضافية للتحقق.”

مشاكل شائعة مع navigator.onLine

بحث سريع على الويب عن “navigator online not working” يكشف عن العديد من منشورات المنتديات حيث واجه أولئك الذين يعتمدون على هذه الخاصية مشاكل.

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

الحل المتقدم: استخدام Fetch API وجافاسكريبت غير المتزامن

تتمثل الفكرة في إجراء طلب (request) والتعامل معه بسلاسة مع اعتراض الأخطاء (error catching) إذا فشل. إذا نجح الطلب، فنحن متصلون بالإنترنت، وإذا فشل، فنحن غير متصلين. سنقوم بطلب صورة صغيرة على فترات زمنية لتحديد حالة الاتصال بالإنترنت. يوفر جافاسكريبت الحديث واجهة برمجة تطبيقات Fetch API والبرمجة غير المتزامنة باستخدام Async و Await. سنستخدم هذه الأدوات لتحقيق هدفنا.

إنشاء دالة checkOnlineStatus()

دعنا نبدأ بإنشاء دالة سهمية (arrow function) غير متزامنة (async) تُسمى checkOnlineStatus. ستقوم هذه الدالة بإرجاع قيمة منطقية (true أو false) تماماً كما تفعل خاصية online لواجهة navigator.

داخل الدالة، سنقوم بإعداد كتلة try حيث ننتظر (await) طلب fetch لصورة بحجم بكسل واحد. تأكد من أن عامل الخدمة (service worker) الخاص بك لا يقوم بتخزين هذه الصورة مؤقتاً. تشير رموز استجابة HTTP بين 200 و 299 إلى النجاح، وسنقوم بإرجاع نتيجة مقارنة رمز الحالة. ستكون هذه القيمة true إذا كان رمز حالة الاستجابة يتراوح بين 200 و 299، و false بخلاف ذلك.

يجب علينا أيضاً توفير كتلة catch التي تلتقط الخطأ إذا فشل الطلب. سنقوم بإرجاع false في كتلة catch للإشارة إلى أننا غير متصلين بالإنترنت بالتأكيد إذا حدث هذا.


const checkOnlineStatus = async () => {
  try {
    const online = await fetch( "/1pixel.png" );
    return online.status >= 200 && online.status < 300 ; // either true or false
  } catch (err) {
    return false ; // definitely offline
  }
};

تحديث حالة الاتصال بشكل دوري

بعد ذلك، سنستخدم الدالة setInterval ونمرر لها دالة غير متزامنة مجهولة (anonymous async function). ستنتظر الدالة غير المتزامنة (await) نتيجة دالتنا checkOnlineStatus. ثم سنستخدم عبارة شرطية ثلاثية مع النتيجة لعرض حالة الاتصال الحالية.

لاختبار هذا المثال، اضبط تأخير الفاصل الزمني (interval delay) ليكون كل 3 ثوانٍ (3000 مللي ثانية). ومع ذلك، هذا التكرار يعتبر مفرطاً في الاستخدام الفعلي. قد يكون التحقق كل 30 ثانية (30000 مللي ثانية) كافياً لاحتياجاتك الحقيقية.


setInterval ( async () => {
  const result = await checkOnlineStatus();
  const statusDisplay = document.getElementById( "status" );
  statusDisplay.textContent = result ? "Online" : "OFFline" ;
}, 3000 ); // probably too often, try 30000 for every 30 seconds

مع حفظ الكود الجديد، دعنا نعود إلى علامة التبويب Application في أدوات مطوري Chrome Dev Tools لاختبار الاستجابة في وضع عدم الاتصال.
لقطة شاشة لأدوات مطوري Chrome تظهر مربع Offline المحدد لاختبار حالة الاتصال باستخدام Fetch API.

معالجة حدث load باستخدام Async

كاد يغيب عن بالنا تضمين مستمع حدث load مع وظائف async! ربما يكون الكشف عن حدث load مهماً فقط إذا كان لديك تطبيق ويب تقدمي (Progressive Web App - PWA) يستخدم عامل خدمة (service worker) لتوفير إمكانية الوصول في وضع عدم الاتصال. بخلاف ذلك، لن يتم تحميل صفحة الويب أو تطبيقك ببساطة بدون اتصال. إليك مستمع حدث load الجديد:


window.addEventListener( "load" , async (event) => {
  const statusDisplay = document.getElementById( "status" );
  statusDisplay.textContent = ( await checkOnlineStatus()) ? "Online" : "OFFline" ;
});

نصيحة أخيرة: التحقق الفوري قبل الطلبات الحرجة

الكود السابق الذي يستخدم الفاصل الزمني (interval) جيد لعرض حالة الاتصال في تطبيقك. ومع ذلك، لا أنصح بالاعتماد على حالة اتصال تم التحقق منها قبل 20 أو 30 ثانية من إجراء طلب بيانات حرج في تطبيقك. لذلك، يجب عليك استدعاء دالة checkOnlineStatus مباشرة قبل الطلب وتقييم الاستجابة قبل طلب البيانات.


const yourDataRequestFunction = async () => {
  const online = await checkOnlineStatus();
  if (online) {
    // make data request
  }
}

الخلاصة التقنية

على الرغم من أن خاصية navigator.onLine مدعومة على نطاق واسع، إلا أنها تقدم نتائج غير موثوقة عند محاولة تحديد ما إذا كانت تطبيقاتنا متصلة بالإنترنت حقاً. من خلال الاستفادة من واجهة برمجة تطبيقات Fetch API وجافاسكريبت غير المتزامن (Async JavaScript)، يمكننا بناء حل أكثر موثوقية ودقة بسرعة. هذا الحل يضمن أن التطبيق يتفاعل بشكل صحيح مع حالة الاتصال الفعلية بالإنترنت، وهو أمر بالغ الأهمية لتجربة المستخدم وتكامل البيانات، خاصة في سياق تطبيقات الويب التقدمية (PWA) التي تعتمد على العمل في وضع عدم الاتصال. إن تبني هذه الطريقة يجنب المطورين الوقوع في فخ الإيجابيات الكاذبة التي قد تنتج عن الاعتماد الكلي على navigator.onLine.

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *