هر آنچه که باید درمورد Service Worker و Web Worker ها بدانید


شاید شما هم در مورد قابلیت جدید ورکرها (Worker) که به مرورگرها اضافه شده است شنیده باشید. در این مقاله تلاش می‌کنیم نگاهی دقیق‌تر به این قابلیت داشته باشیم.

شما می‌توانید با استفاده از ورکر‌ها قابلیت چندرشته‌ای را (multi thread) را درون مرورگرتان داشته باشید ولی شاید این چند‌رشته‌ای با چند‌رشته‌ای‌های دیگری که در زبان‌های برنامه‌نویسی دیگر مشاهده کرده‌اید متفاوت باشد. زیرا در بسیاری از زبان‌ها ساختار چند‌رشته‌ای به‌صورتی است که شما چند رشته‌ی مختلف را اجرا می‌کنید و همه‌ی این رشته‌ها به توابع و ابجکت‌های (Object) مشترکی دسترسی دارند که مسائلی و مشکلاتی را ایجاد می‌کند و برنامه‌نویس باید مراقب استفاده از آن‌ها باشد تا کلاس‌ها و توابعش thread-safe باشند و به مشکلی برخورد نکنند. این در حالی ست که ورکر‌ها در جاوااسکریپت به ابجکت‌های non-thread-safe دسترسی ندارند (مانند DOM ها) و انتقال اطلاعات بین این ورکر‌ها نیز فقط توسط ابجکت‌های قابل serialize شدن ردوبدل می‌شود و بصورت انتقال کامل یا کپی کردن صورت می‌گیرد. این بدان معناست که نمی‌توان ابجکت‌ها را بین ورکر‌ها به اشتراک گذاشت که باعث می‌شود همه چیز thread-safe باشد.

حالا که دانستیم ورکر برای چیست و چه محدودیت‌هایی دارد بهتر است با هم سری به کد بزنیم.

برای ساخت یک ورکر جدید به شکل زیر عمل می‌کنیم. (ورکرها را می‌توان در رشته اصلی و یا ورکرهای دیگر ساخت.)

var myWorker = new Worker('worker.js');

همانطور که می‌بینید برای ساخت یک ورکر کافی است سازنده (constructor) آن را با آدرس یک فایل اسکریپت (script) اجرا کنیم تا ورکر شروع به دانلود و اجرای آن اسکریپت کند.

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

// main script
var myWorker = new Worker('worker.js');
myWorker.postMessage([first.value, second.value]);
myWorker. = function(e) {
  result.textContent = e.data;
  console.log('Message received from worker');
}
// worker.js
self. = function(e) {
  console.log('Message received from main script');
  var workerResult = 'Result: ' + (e.data[0] * e.data[1]);
  console.log('Posting message back to main script');
  self.postMessage(workerResult);
}

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

پس از آنکه کارتان با ورکر موردنظر تمام شد نیاز است تا به کار آن خاتمه دهید که با دستور زیر قابل انجام است.

 myWorker.terminate()

ورکرها انواع مختلفی همچون DedicatedWorker ، SharedWorker و ServiceWorker دارند، در SharedWorker ها ورکر ساخته‌شده می‌تواند در بین چند worker/window/iframe به طور اشتراکی استفاده شود در حالی که DedicatedWorker فقط در جایی که ساخته شده است قابل استفاده است. در بالا ما از DedicatedWorker استفاده کردیم ولی قواعد کلی برای SharedWorker ها نیز برقرار است.

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

در ادامه نگاهی دقیق‌تر به سرویس ورکر (Service Worker) خواهیم داشت.

سرویس ورکر نوع خاصی از ورکر است که می‌تواند رابط بین سایت ما و مرورگر باشد. این ورکر می‌تواند وظایفی را که نیاز به رابط کاربری user-interface ندارد به‌صورت پس‌زمینه (background) انجام دهد. همچنین با توجه به آنکه اجرای آن نیازمند یک پنجره‌ی فعال از سایت شما نیست، مرورگر می‌تواند حتی در صورت بسته بودن تمام پنجره‌ها نیز در صورت نیاز با این ورکر صحبت کند و درخواست‌هایی را برای ما انجام دهد.

برای مثال قابلیت هایی که با استفاده از سرویس ورکر می‌توانیم به آن‌ها دست یابیم شامل:
- offline-experiences : باز شدن و استفاده از نرم‌افزار تحت وب بدون داشتن اینترنت
- background-syncs :‌ انجام برخی عملیات‌های همگام‌سازی در پس‌زمینه
- push notifications : دریافت و نمایش اعلان از سرور بدون نیاز به باز بودن سایت

به‌صورت کلی بیشتر رفتارهای این ورکر همانند ورکرهای عادی با استفاده از , postMessage است ولی این ورکر می‌تواند به عنوان network proxy نیز عمل کند و با استفاده از رخداد onfetch به درخواست‌های صادر شده از سایت جواب دهد. همچنین می‌تواند عملیات‌هایی همچون caching نیز انجام دهد.

چرخه ی عمر :

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

if ('serviceWorker' in navigator) {
  window.addEventListener('load', function () {
    navigator.serviceWorker.register('/sw.js').then(
      function (registration) {
        // Registration was successful
        console.log('ServiceWorker registration successful');
      },
      function (err) {
        // registration failed :(
        console.log('ServiceWorker registration failed');
      },
    );
  });
}
چرخه ی عمر سرویس ورکر
چرخه ی عمر سرویس ورکر

همانطور که در نمودار بالا مشاهده می‌کنید در صورتی که نصب سرویس ورکر شما موفقیت‌آمیز باشد پس از آن سرویس ورکر به حالت idle می‌رود و در صورتی که درخواستی با message و یا fetch نداشته باشد برای کاهش مصرف رم (RAM) متوقف می‌شود و تمامی متغیرهای محلی آن از حافظه‌ی رم پاک می‌شوند. به همین علت لازم است در صورتی که می‌خواهید دیتایی (Data) رو در بین ری‌استارت‌های مختلف نگهداری کنید لازم است که آن ها را در IndexedDB ذخیره کنید و سپس در استارت بعدی مقادیر مورد نیاز را از آن بخوانید.

برای مشاهده‌ی لیست سرویس ورکر‌های نصب‌شده در مرورگرتان می‌توانید به آدرس chrome://inspect/#service-workers مراجعه کنید.

در کد سرویس ورکر می‌توانید با دریافت رخداد install مراحل نصبی که مورد نظرتان است شامل cache کردن فایل های استاتیک و یا کارهای دیگر را انجام دهید.

self.addEventListener('install', function(event) {
  const promise = doInstallSteps();
  event.waitUntil(promise);
});

در قطعه‌کد بالا از تابع waitUntil استفاده شده است. وظیفه‌ی این تابع دریافت کردن یک promise می‌باشد تا متوقف شدن سرویس ورکر را تا زمان به نتیجه رسیدن پرامیس موردنظر به تاخیر بیاندازد.

برای مدیریت راحت‌تر cache و offline-experience می‌توانید از لایبرری workbox استفاده نمایید.

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

منابع و مطالعه‌ی بیشتر :
توضیحات وب‌ورکر در سایت MDN
توضیحات سرویس ورکر در سایت developers.google