ارتباط دوطرفه با کارگران خدماتی

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

در پشت صحنه، این کتابخانه از یک API مرورگر استفاده می‌کند که در بخش بعدی آن را بررسی خواهیم کرد: کانال پیام، اما بسیاری از جزئیات پیاده‌سازی را انتزاعی می‌کند و استفاده از آن را آسان‌تر می‌سازد، درحالی‌که از پشتیبانی گسترده مرورگر که این API دارد بهره می‌برد.

نموداری که ارتباط دوطرفه بین صفحه و کارمند خدمات را بااستفاده از Workbox Window نشان می‌دهد.

استفاده از میاناهای برنامه‌سازی کاربردی مرورگر

اگر کتابخانه Workbox برای نیازهای شما کافی نیست، چندین میانای برنامه‌سازی کاربردی سطح پایین‌تر برای پیاده‌سازی ارتباط «دو طرفه» بین صفحه‌ها و کارگران خدمات دردسترس است. آن‌ها شباهت‌ها و تفاوت‌هایی دارند:

شباهت‌ها:

  • در همه موارد، ارتباط از یک طرف ازطریق میانای postMessage() شروع می‌شود و در طرف دیگر با پیاده‌سازی یک گرداننده message دریافت می‌شود.
  • در عمل، همه میاناهای برنامه‌سازی کاربردی دردسترس به ما امکان می‌دهند موارد استفاده یکسانی را پیاده‌سازی کنیم، اما برخی‌از آن‌ها ممکن است توسعه را در برخی سناریوها ساده‌تر کنند.

تفاوت‌ها:

  • آن‌ها روش‌های مختلفی برای شناسایی طرف دیگر ارتباط دارند: برخی‌از آن‌ها از ارجاع صریح به بافت دیگر استفاده می‌کنند، درحالی‌که برخی دیگر می‌توانند به‌طور ضمنی ازطریق شیء پراکسی که در هر طرف نمونه‌سازی شده است ارتباط برقرار کنند.
  • پشتیبانی مرورگر در آن‌ها متفاوت است.
نموداری که ارتباط دوطرفه بین صفحه و کارگر سرویس و میاناهای برنامه‌سازی کاربردی مرورگر دردسترس را نشان می‌دهد.

Broadcast Channel API

Browser Support

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

Source

Broadcast Channel API امکان برقراری ارتباط پایه بین زمینه‌های مرور را ازطریق BroadcastChannel objects فراهم می‌کند.

برای پیاده‌سازی آن، ابتدا هر بافت باید یک شیء 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...
  }
};

همان‌طور که می‌بینید، هیچ ارجاع صریحی به بافت خاصی وجود ندارد، بنابراین نیازی به دریافت ارجاع به عامل خدماتی یا هر مشتری خاصی نیست.

نموداری که ارتباط دوطرفه بین صفحه و کارگر سرویس را بااستفاده از شیء «کانال پخش» نشان می‌دهد.

نکته منفی این است که در زمان نوشتن این مطلب، این میانای برنامه‌سازی کاربردی از Chrome،‏ Firefox، و Edge پشتیبانی می‌کند، اما مرورگرهای دیگر، مثل Safari، هنوز از آن پشتیبانی نمی‌کنند.

میانای برنامه‌سازی کاربردی کارخواه

Browser Support

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

Source

Client API به شما امکان می‌دهد مرجعی برای همه اشیاء 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
  }
});

برای برقراری ارتباط با هریک از کارخواهان، کارمند خدمات با اجرای روش‌هایی مثل Clients.matchAll() و Clients.get()، آرایه‌ای از اشیاء WindowClient را به‌دست می‌آورد. سپس می‌تواند 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 is a good option to communicate easily with all the active tabs from a service worker in a relatively straightforward way. این میانای برنامه‌سازی کاربردی توسط همه مرورگرهای اصلی پشتیبانی می‌شود، اما ممکن است همه روش‌های آن دردسترس نباشد، بنابراین حتماً قبل‌از پیاده‌سازی آن در سایتتان، پشتیبانی مرورگر را بررسی کنید.

کانال پیام

Browser Support

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

Source

کانال پیام برای ایجاد کانال ارتباطی دوطرفه، به تعریف و ارسال درگاه از یک زمینه به زمینه دیگر نیاز دارد.

برای مقداردهی اولیه کانال، صفحه یک شیء 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 از همه مرورگرهای اصلی پشتیبانی می‌کند.

میاناهای برنامه‌سازی کاربردی پیشرفته: همگام‌سازی پس‌زمینه و واکشی پس‌زمینه

در این راهنما، روش‌های پیاده‌سازی تکنیک‌های ارتباطی دوطرفه را برای موارد نسبتاً ساده بررسی کردیم، مانند ارسال یک پیام رشته‌ای که عملیات اجرایی را توصیف می‌کند، یا فهرستی از نشانی‌های وب برای ذخیره در حافظه پنهان از یک زمینه به زمینه دیگر. در این بخش، دو میانای برنامه‌سازی کاربردی را برای رسیدگی به سناریوهای خاص بررسی خواهیم کرد: عدم اتصال و بارگیری‌های طولانی.

همگام‌سازی پس‌زمینه

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() باید یک promise برگرداند که نشان‌دهنده موفقیت/شکست هر کاری است که تلاش می‌کند انجام دهد. اگر این promise برآورده شود، همگام‌سازی کامل است. اگر ناموفق باشد، همگام‌سازی دیگری برای امتحان مجدد زمان‌بندی خواهد شد. همگام‌سازی‌های مجدد نیز منتظر اتصال می‌مانند و از عقب‌نشینی نمایی استفاده می‌کنند.

پس‌از انجام عملیات، عامل خدماتی می‌تواند بااستفاده از هرکدام از میاناهای برنامه‌سازی کاربردی ارتباطی که قبلاً بررسی شد، با صفحه ارتباط برقرار کند تا واسط کاربر را به‌روز کند.

«جستجوی Google» از «همگام‌سازی پس‌زمینه» برای حفظ پُرسمان‌های ناموفق به‌دلیل اتصال بد استفاده می‌کند و بعداً وقتی کاربر آنلاین است، آن‌ها را دوباره امتحان می‌کند. وقتی عملیات انجام شد، نتیجه را ازطریق اعلان لحظه‌ای وب به کاربر اطلاع می‌دهند:

نموداری که نشان می‌دهد یک صفحه درحال ارسال درگاه به یک کارگر سرویس است تا ارتباط دوطرفه برقرار شود.

واکشی پس‌زمینه

Browser Support

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

Source

برای کارهای نسبتاً کوتاه مانند ارسال پیام یا فهرستی از نشانی‌های وب برای ذخیره در حافظه پنهان، گزینه‌هایی که تاکنون بررسی شده‌اند انتخاب خوبی هستند. اگر تکلیف خیلی طول بکشد، مرورگر کارگر سرویس را ازبین می‌برد، درغیراین‌صورت حریم خصوصی و باتری کاربر به‌خطر می‌افتد.

Background Fetch API به شما امکان می‌دهد کارهای طولانی را به یک سرویس‌کار واگذار کنید، مانند بارگیری فیلم، پادکست، یا مراحل یک بازی.

برای برقراری ارتباط با کارمند خدمات از صفحه، به‌جای postMessage() از backgroundFetch.fetch استفاده کنید:

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}%`);
});
نموداری که نشان می‌دهد یک صفحه درحال ارسال درگاه به یک کارگر سرویس است تا ارتباط دوطرفه برقرار شود.
میانای کاربر به‌روز شده است تا پیشرفت بارگیری را نشان دهد (سمت راست). به‌لطف کارگران سرویس، وقتی همه برگه‌ها بسته شده باشند، عملیات می‌تواند همچنان اجرا شود (سمت چپ).

مراحل بعدی

در این راهنما، عمومی‌ترین حالت ارتباط بین صفحه و سرویس‌کار را بررسی کردیم (ارتباط دوطرفه).

در بسیاری از مواقع، ممکن است فرد فقط به یک زمینه برای برقراری ارتباط با دیگری نیاز داشته باشد، بدون اینکه پاسخی دریافت کند. برای راهنمایی درباره نحوه پیاده‌سازی تکنیک‌های یک‌سویه در صفحه‌هایتان از و به سرویس‌کار، همراه با موارد استفاده و نمونه‌های تولید، راهنماهای زیر را بررسی کنید: