التواصل الثنائي مع عاملي الخدمة

Andrew Guan
Andrew Guan
Demián Renzulli
Demián Renzulli

في بعض الحالات، قد يحتاج تطبيق الويب إلى إنشاء قناة اتصال ثنائية الاتجاه بين الصفحة ومشغّل الخدمات.

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

في هذا الدليل، سنستكشف الطرق المختلفة لتنفيذ اتصال ثنائي الاتجاه بين سياق النافذة ومشغّل الخدمات، من خلال استكشاف واجهات برمجة التطبيقات المختلفة ومكتبة Workbox، بالإضافة إلى بعض الحالات المتقدّمة.

مخطّط بياني يوضّح تبادل الرسائل بين مشغّل الخدمات والصفحة.

استخدام Workbox

‫workbox-window هي مجموعة من الوحدات في مكتبة Workbox المخصّصة للتشغيل في سياق النافذة. توفّر فئة Workbox طريقة messageSW() لإرسال رسالة إلى مشغّل الخدمات المسجَّل في المثيل وانتظار الردّ.

ينشئ رمز الصفحة التالي مثيل Workbox جديدًا ويرسل رسالة إلى مشغّل الخدمات للحصول على إصداره:

const wb = new Workbox('/sw.js');
wb.register();

const swVersion = await wb.messageSW({type: 'GET_VERSION'});
console.log('Service Worker version:', swVersion);

ينفّذ مشغّل الخدمات مستمعًا للرسائل على الطرف الآخر، ويستجيب لمشغّل الخدمات المسجّل:

const SW_VERSION = '1.0.0';

self.addEventListener('message', (event) => {
  if (event.data.type === 'GET_VERSION') {
    event.ports[0].postMessage(SW_VERSION);
  }
});

تستخدم المكتبة واجهة برمجة تطبيقات للمتصفّح سنراجعها في القسم التالي: قناة الرسائل، ولكنّها تلخّص العديد من تفاصيل التنفيذ، ما يسهّل استخدامها، مع الاستفادة من التوافق الواسع مع المتصفّحات الذي توفّره واجهة برمجة التطبيقات هذه.

مخطّط بياني يعرض الاتصال الثنائي الاتجاه بين الصفحة وعامل الخدمة باستخدام Workbox Window

استخدام واجهات برمجة التطبيقات للمتصفّح

إذا لم تكن مكتبة Workbox كافية لتلبية احتياجاتك، تتوفّر عدة واجهات برمجة تطبيقات منخفضة المستوى لتنفيذ الاتصال "ثنائي الاتجاه" بين الصفحات وعاملي الخدمة. لديهما بعض أوجه التشابه والاختلاف:

أوجه التشابه:

  • في جميع الحالات، يبدأ الاتصال من أحد الطرفين عبر واجهة postMessage() ويتم تلقّيه على الطرف الآخر من خلال تنفيذ معالج message.
  • من الناحية العملية، تتيح لنا جميع واجهات برمجة التطبيقات المتاحة تنفيذ حالات الاستخدام نفسها، ولكن قد يسهّل بعضها عملية التطوير في بعض السيناريوهات.

الاختلافات:

  • وتتوفّر طرق مختلفة لتحديد الطرف الآخر من الاتصال: يستخدم بعضها مرجعًا صريحًا للسياق الآخر، بينما يمكن للبعض الآخر الاتصال ضمنيًا من خلال كائن وكيل يتم إنشاء مثيل له على كل جانب.
  • ويختلف توافق المتصفّحات بينها.
مخطّط بياني يعرض الاتصال الثنائي الاتجاه بين الصفحة وعامل الخدمة وواجهات برمجة التطبيقات المتاحة في المتصفّح

واجهة برمجة التطبيقات Broadcast Channel API

Browser Support

  • Chrome: 54.
  • Edge: 79.
  • Firefox: 38.
  • Safari: 15.4.

Source

تتيح واجهة برمجة تطبيقات Broadcast Channel إجراء عمليات تواصل أساسية بين سياقات التصفّح من خلال عناصر BroadcastChannel.

لتنفيذ ذلك، يجب أولاً أن ينشئ كل سياق كائن BroadcastChannel برقم التعريف نفسه وأن يرسل الرسائل منه ويتلقّاها:

const broadcast = new BroadcastChannel('channel-123');

يعرض عنصر BroadcastChannel واجهة postMessage() لإرسال رسالة إلى أي سياق استماع:

//send message
broadcast.postMessage({ type: 'MSG_ID', });

يمكن لأي سياق متصفّح الاستماع إلى الرسائل من خلال طريقة onmessage لكائن BroadcastChannel:

//listen to messages
broadcast.onmessage = (event) => {
  if (event.data && event.data.type === 'MSG_ID') {
    //process message...
  }
};

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

مخطّط بياني يعرض الاتصال الثنائي الاتجاه بين الصفحة وعامل الخدمة، باستخدام عنصر Broadcast Channel.

أما العيب فيتمثّل في أنّ واجهة برمجة التطبيقات هذه، في وقت كتابة هذه المقالة، متوافقة مع Chrome وFirefox وEdge، ولكنّها غير متوافقة مع متصفّحات أخرى، مثل Safari.

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

Browser Support

  • Chrome: 40.
  • Edge: 17.
  • Firefox: 44.
  • Safari: 11.1.

Source

تتيح لك واجهة برمجة تطبيقات العميل الحصول على مرجع لجميع عناصر WindowClient التي تمثّل علامات التبويب النشطة التي يتحكّم فيها مشغّل الخدمات.

بما أنّ الصفحة يتحكّم فيها مشغّل خدمات واحد، فإنّها تستمع إلى الرسائل وترسلها إلى مشغّل الخدمات النشط مباشرةً من خلال واجهة serviceWorker:

//send message
navigator.serviceWorker.controller.postMessage({
  type: 'MSG_ID',
});

//listen to messages
navigator.serviceWorker.onmessage = (event) => {
  if (event.data && event.data.type === 'MSG_ID') {
    //process response
  }
};

وبالمثل، يستمع مشغّل الخدمات إلى الرسائل من خلال تنفيذ أداة استماع onmessage:

//listen to messages
self.addEventListener('message', (event) => {
  if (event.data && event.data.type === 'MSG_ID') {
    //Process message
  }
});

للتواصل مع أي من عملائه، يحصل مشغّل الخدمات على مصفوفة من عناصر WindowClient من خلال تنفيذ طرق مثل Clients.matchAll() وClients.get(). ويمكنه بعد ذلك postMessage() أيًّا منها:

//Obtain an array of Window client objects
self.clients.matchAll(options).then(function (clients) {
  if (clients && clients.length) {
    //Respond to last focused tab
    clients[0].postMessage({type: 'MSG_ID'});
  }
});
مخطّط بياني يعرض عاملاً في الخدمة يتواصل مع مجموعة من العملاء

تُعدّ Client API خيارًا جيدًا للتواصل بسهولة مع جميع علامات التبويب النشطة من مشغّل خدمات بطريقة مباشرة نسبيًا. تتوافق واجهة برمجة التطبيقات مع جميع المتصفّحات الرئيسية، ولكن قد لا تتوفّر بعض طرقها، لذا تأكَّد من مراجعة المتصفّحات المتوافقة قبل تنفيذها في موقعك الإلكتروني.

قناة الرسائل

Browser Support

  • Chrome: 2.
  • Edge: 12.
  • Firefox: 41.
  • Safari: 5.

Source

تتطلّب واجهة برمجة التطبيقات Message Channel تحديد منفذ وتمريره من سياق إلى آخر لإنشاء قناة اتصال ثنائية الاتجاه.

لتهيئة القناة، تنشئ الصفحة كائن MessageChannel وتستخدمه لإرسال منفذ إلى مشغّل الخدمات المسجَّل. تنفّذ الصفحة أيضًا أداة معالجة onmessage لتلقّي الرسائل من السياق الآخر:

const messageChannel = new MessageChannel();

//Init port
navigator.serviceWorker.controller.postMessage({type: 'PORT_INITIALIZATION'}, [
  messageChannel.port2,
]);

//Listen to messages
messageChannel.port1.onmessage = (event) => {
  // Process message
};
مخطّط بياني يعرض صفحة تمرّر منفذًا إلى مشغّل خدمات لإنشاء اتصال ثنائي الاتجاه

يتلقّى مشغّل الخدمات المنفذ ويحفظ مرجعًا له ويستخدمه لإرسال رسالة إلى الطرف الآخر:

let communicationPort;

//Save reference to port
self.addEventListener('message', (event) => {
  if (event.data && event.data.type === 'PORT_INITIALIZATION') {
    communicationPort = event.ports[0];
  }
});

//Send messages
communicationPort.postMessage({type: 'MSG_ID'});

يتوافق MessageChannel حاليًا مع جميع المتصفحات الرئيسية.

واجهات برمجة التطبيقات المتقدّمة: المزامنة في الخلفية والجلب في الخلفية

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

المزامنة في الخلفية

Browser Support

  • Chrome: 49.
  • Edge: 79.
  • Firefox: not supported.
  • Safari: not supported.

Source

قد يحتاج تطبيق المحادثات إلى التأكّد من عدم فقدان الرسائل بسبب ضعف الاتصال. تتيح لك واجهة برمجة التطبيقات Background Sync API تأجيل الإجراءات لإعادة محاولة تنفيذها عندما يكون لدى المستخدم إمكانية اتصال مستقرة. هذا مفيد لضمان إرسال أي شيء يريد المستخدم إرساله بالفعل.

بدلاً من واجهة postMessage()، تسجّل الصفحة sync:

navigator.serviceWorker.ready.then(function (swRegistration) {
  return swRegistration.sync.register('myFirstSync');
});

بعد ذلك، يستمع مشغّل الخدمات إلى الحدث sync لمعالجة الرسالة:

self.addEventListener('sync', function (event) {
  if (event.tag == 'myFirstSync') {
    event.waitUntil(doSomeStuff());
  }
});

يجب أن تعرض الدالة doSomeStuff() وعدًا يشير إلى نجاح أو فشل ما تحاول تنفيذه. إذا تم الوفاء بالوعد، تكتمل المزامنة. وفي حال تعذّر ذلك، ستتم جدولة مزامنة أخرى لإعادة المحاولة. تنتظر عمليات إعادة المزامنة أيضًا الاتصال بالشبكة، وتستخدم خوارزمية الرقود الأسي الثنائي.

بعد تنفيذ العملية، يمكن لـ Service Worker التواصل مع الصفحة لتعديل واجهة المستخدم، وذلك باستخدام أي من واجهات برمجة التطبيقات الخاصة بالتواصل التي تم استكشافها سابقًا.

يستخدم محرّك بحث Google ميزة "المزامنة في الخلفية" للاحتفاظ بطلبات البحث التي تعذّر إجراؤها بسبب ضعف الاتصال، وإعادة محاولة إجرائها لاحقًا عندما يكون المستخدم متصلاً بالإنترنت. بعد تنفيذ العملية، يتم إبلاغ المستخدم بالنتيجة من خلال إشعار فوري على الويب:

مخطّط بياني يعرض صفحة تمرّر منفذًا إلى مشغّل خدمات لإنشاء اتصال ثنائي الاتجاه

استرجاع البيانات في الخلفية

Browser Support

  • Chrome: 74.
  • Edge: 79.
  • Firefox: not supported.
  • Safari: not supported.

Source

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

تتيح لك واجهة برمجة التطبيقات Background Fetch API تفويض مهمة يستغرق تنفيذها وقتًا طويلاً إلى مشغّل خدمات، مثل تنزيل الأفلام أو ملفات البودكاست أو مستويات لعبة معيّنة.

للتواصل مع مشغّل الخدمات من الصفحة، استخدِم backgroundFetch.fetch بدلاً من postMessage():

navigator.serviceWorker.ready.then(async (swReg) => {
  const bgFetch = await swReg.backgroundFetch.fetch(
    'my-fetch',
    ['/ep-5.mp3', 'ep-5-artwork.jpg'],
    {
      title: 'Episode 5: Interesting things.',
      icons: [
        {
          sizes: '300x300',
          src: '/ep-5-icon.png',
          type: 'image/png',
        },
      ],
      downloadTotal: 60 * 1024 * 1024,
    },
  );
});

يسمح العنصر BackgroundFetchRegistration للصفحة بالاستماع إلى الحدث progress لمتابعة تقدّم عملية التنزيل:

bgFetch.addEventListener('progress', () => {
  // If we didn't provide a total, we can't provide a %.
  if (!bgFetch.downloadTotal) return;

  const percent = Math.round(
    (bgFetch.downloaded / bgFetch.downloadTotal) * 100,
  );
  console.log(`Download progress: ${percent}%`);
});
مخطّط بياني يعرض صفحة تمرّر منفذًا إلى مشغّل خدمات لإنشاء اتصال ثنائي الاتجاه
تم تعديل واجهة المستخدم للإشارة إلى مستوى تقدّم عملية التنزيل (على اليمين). بفضل مشغّلي الخدمات، يمكن مواصلة العملية بعد إغلاق جميع علامات التبويب (على اليسار).

الخطوات التالية

في هذا الدليل، استكشفنا الحالة الأكثر عمومية للتواصل بين الصفحة وعاملي الخدمة (التواصل الثنائي الاتجاه).

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

  • دليل التخزين المؤقت الإلزامي: استدعاء مشغّل خدمات من الصفحة لتخزين الموارد مؤقتًا مسبقًا (على سبيل المثال، في سيناريوهات الجلب المُسبق)
  • بث التعديلات: استدعاء الصفحة من مشغّل الخدمات لإعلامها بالتعديلات المهمة (مثل توفّر إصدار جديد من تطبيق الويب)