عقلية عامل الخدمات

طريقة التفكير عند التفكير في عمال الخدمة.

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

غير أنّ مقدّمي الخدمات يختلفون عن أي شيء اعتاد عليه معظمنا من مطوّري برامج الويب. وهي تتضمن منحنى تعلم شديد الانحدار وعددًا من العقبات التي عليك الانتباه إليها.

تعاونتُ أنا وGoogle Developers مؤخرًا على مشروع، وهو مشروع Service Workies، هو لعبة مجانية لفهم موظفي الخدمات. أثناء بنائه والعمل مع خصوصيات وعموميات عمال الخدمة المعقدة، واجهت بعض العقبات. أكثر ما ساعدني هو ابتكار عدد من الاستعارات التصويرية. في هذه المشاركة، سنستكشف هذه النماذج الذهنية ونلفّ عقولنا حول السمات المتناقضة التي تجعل العاملين في الخدمة معقدين ورائعين في الوقت نفسه.

نفس الشيء، لكن مختلف

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

لكن سلوكيات عاملي الخدمات الأخرى تجعلك تحك رأسك في حالة من الارتباك. خاصةً عند إعادة تحميل الصفحة وعدم تطبيق تغييرات الرمز.

طبقة جديدة

عادةً عند إنشاء موقع، تكون هناك طبقتان فقط يجب التفكير فيهما: العميل والخادم. عامل الخدمات هو طبقة جديدة تمامًا تقع في المنتصف.

عامل الخدمة يعمل كطبقة وسيطة بين العميل والخادم

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

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

في لعبة Service Workies، نتناول التفاصيل العديدة لدورة حياة عاملي الخدمات ونقدّم لك قدرًا كبيرًا من الممارسة.

فعّال ولكن محدود

إن وجود عامل خدمات على موقعك يمنحك فوائد مذهلة. يمكن لموقعك الإلكتروني:

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

  • localStorage
  • نموذج DOM
  • النافذة

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

طويل الأجل، لكنه قصير الأجل

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

في Service Workies، نضع تصورًا لهذا المفهوم مع Kolohe (عامل خدمات ودودة) يعترض الطلبات ويعالجها.

متوقفة

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

waitUntil

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

يوضّح هذا المثال للمتصفِّح أنّ مشغّل الخدمات لم يكتمل تثبيت إلا بعد إنشاء ذاكرة التخزين المؤقت لتطبيق assets وتعبئتها بصورة سيف:

self.addEventListener("install", event => {
  event.waitUntil(
    caches.open("assets").then(cache => {
      return cache.addAll(["/weapons/sword/blade.png"]);
    })
  );
});

انتبِه للحالة العالمية

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

ضع في الاعتبار هذا المثال الذي يستخدم حالة عمومية:

const favoriteNumber = Math.random();
let hasHandledARequest = false;

self.addEventListener("fetch", event => {
  console.log(favoriteNumber);
  console.log(hasHandledARequest);
  hasHandledARequest = true;
});

في كل طلب، سيسجِّل عامل الخدمة هذا رقمًا، لنفترض أنه 0.13981866382421893. يتغير المتغيّر hasHandledARequest أيضًا إلى true. وفي هذه الحالة، يظل مشغّل الخدمة غير نشِط لفترة قصيرة، وبالتالي يتوقف المتصفّح عن العمل. وفي المرة التالية التي يحدث فيها طلب، تكون هناك حاجة إلى مشغّل الخدمة مرة أخرى، ومن ثمَّ ينشطه المتصفح. يتم تقييم مرة أخرى نصها. والآن، تمت إعادة ضبط hasHandledARequest على false، وأصبح favoriteNumber مختلفًا تمامًا، 0.5907281835659033.

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

في الفصل 3 من Service Workies، يظهر عامل تشغيل الخدمة الذي توقف عن العمل بأنّه يفقد كل الألوان أثناء انتظار تنشيطه.

رسم بياني لمشغل خدمات متوقف

معًا، ولكن منفصلة

لا يمكن التحكّم في صفحتك إلا من قِبل مشغّل خدمات واحد في كل مرة. ولكن يمكن أن يتم تثبيت مشغّلَي خدمة في آنٍ واحد. عند إجراء تغيير على رمز مشغّل الخدمات وإعادة تحميل الصفحة، لا يكون هناك تعديل فعلي على عامل الخدمات على الإطلاق. عمال الخدمة غير قابلين للتغيير. أنت تنشئ فيديو جديدًا تمامًا. سيقوم مشغّل الخدمات الجديد هذا (نطلق عليه SW2) بتثبيت الخدمة ولكنه لن يتم تفعيله بعد. ويجب انتظار إنهاء مشغّل الخدمة الحالي (SW1) (عندما يغادر المستخدم موقعك الإلكتروني).

المراسلة مع ذاكرات التخزين المؤقت لدى عامل خدمات آخر

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

  • قد يحذف SW2 ذاكرة التخزين المؤقت التي يستخدمها SW1 بشكل نشط.
  • يمكن لبرنامج SW2 تعديل محتوى ذاكرة التخزين المؤقت التي يستخدمها SW1، مما يتسبب في استجابة SW1 بمواد عرض لا تتوقعها الصفحة.

تخطي مرحلة الانتظار

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

بدء التنظيف

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

const version = 1;
const assetCacheName = `assets-${version}`;

self.addEventListener("install", event => {
  caches.open(assetCacheName).then(cache => {
    // confidently do stuff with your very own cache
  });
});

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

تصور ذاكرة التخزين المؤقت

إنهاء التنظيف

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

طريقة caches.match() هي اختصار شائع الاستخدام لاسترداد عنصر من أي ذاكرة تخزين مؤقت يظهر فيها تطابق. لكنه يتكرر خلال ذاكرات التخزين المؤقت بالترتيب الذي تم إنشاؤه به. لنفترض أن لديك نسختين من ملف النص البرمجي app.js في ذاكرات تخزين مؤقت مختلفة: assets-1 وassets-2. تتوقع صفحتك النص البرمجي الأحدث الذي تم تخزينه في assets-2. ولكن إذا لم يسبق لك حذف ذاكرة التخزين المؤقت القديمة، سيعرض caches.match('app.js') ذاكرة التخزين المؤقت القديمة من assets-1 وسيؤدّي ذلك على الأرجح إلى إيقاف موقعك الإلكتروني.

كل ما يتطلبه الأمر للتنظيف بعد عاملي الخدمة السابقين هو حذف أي ذاكرة تخزين مؤقت لا يحتاجها عامل الخدمة الجديد:

const version = 2;
const assetCacheName = `assets-${version}`;

self.addEventListener("activate", event => {
  event.waitUntil(
    caches.keys().then(cacheNames => {
      return Promise.all(
        cacheNames.map(cacheName => {
          if (cacheName !== assetCacheName){
            return caches.delete(cacheName);
          }
        });
      );
    });
  );
});

يتطلب منع موظفي الخدمة من مضايقة بعضهم البعض بعضًا من العمل والنظام، ولكنه يستحق العناء.

عقلية عاملي الخدمات

سيساعدك التفكير في التفكير الصحيح أثناء التفكير في عاملي الخدمة على بناء عملك بثقة. ستتمكن من إنشاء تجارب مدهشة للمستخدمين بمجرد أن تتعرف عليها.

إذا كنت تريد فهم كل هذا من خلال تشغيل لعبة، فأنت محظوظ. يمكنك تشغيل لعبة Service Workies حيث ستتعرف على طرق عاملي الخدمات لقتل الحيوانات بلا اتّصال بالإنترنت.