توفير سلاسل محادثات على الويب باستخدام عمال الوحدات

أصبح نقل المهام الثقيلة إلى سلاسل المحادثات في الخلفية أسهل الآن باستخدام وحدات JavaScript في العاملين على الويب.

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

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

وفي ما يلي مثال نموذجي على استخدام العامل، حيث يستمع النص البرمجي للعاملين إلى الرسائل الواردة من الخادم سلسلة محادثات وتستجيب من خلال إرسال رسائل خاصة بها:

page.js:

const worker = new Worker('worker.js');
worker.addEventListener('message', e => {
  console.log(e.data);
});
worker.postMessage('hello');

worker.js:

addEventListener('message', e => {
  if (e.data === 'hello') {
    postMessage('world');
  }
});

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

السجلّ: العمال الكلاسيكيون

تستخدم الدالة الإنشائية للعاملين نموذجًا كلاسيكيًا للنص البرمجي، وهو بالنسبة إلى عنوان URL للمستند. فإنها تعرض على الفور إشارة إلى مثيل العامل الجديد، تعرض واجهة مراسلة بالإضافة إلى طريقة terminate() تتوقف فورًا يؤدي إلى تدمير العامل.

const worker = new Worker('worker.js');

تتوفر دالة importScripts() لدى العاملين على الويب لتحميل رموز إضافية، إلا أنها توقف تنفيذ العامل مؤقتًا لجلب وتقييم كل نص برمجي. كما يقوم بتنفيذ النصوص البرمجية في النطاق العمومي مثل علامة <script> الكلاسيكية، ما يعني أن المتغيّرات في نص برمجي واحد يمكن التعبير عنها بالمتغيرات في موضع آخر.

worker.js:

importScripts('greet.js');
// ^ could block for seconds
addEventListener('message', e => {
  postMessage(sayHello());
});

greet.js:

// global to the whole worker
function sayHello() {
  return 'world';
}

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

إدخال العاملين في الوحدة

وضع جديد للعاملين على الويب يوفّرون ميزات بيئة التشغيل والأداء التي توفّرها JavaScript JavaScript يتم شحنها في Chrome 80، ويُطلق عليها اسم "عاملي الوحدات". تشير رسالة الأشكال البيانية تقبل الدالة الإنشائية Worker الآن خيار {type:"module"} جديد، والذي يغير تحميل النص البرمجي التنفيذ لمطابقة <script type="module">.

const worker = new Worker('worker.js', {
  type: 'module'
});

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

يتيح الانتقال إلى وحدات JavaScript أيضًا استخدام العناصر الديناميكية الاستيراد لرمز التحميل الكسول بدون حظر تنفيذ العامل. الاستيراد الديناميكي أكثر وضوحًا من استخدام importScripts() لتحميل التبعيات، حيث يتم إرجاع تصديرات الوحدة المستوردة بدلاً من الاعتماد على المتغيرات العمومية.

worker.js:

import { sayHello } from './greet.js';
addEventListener('message', e => {
  postMessage(sayHello());
});

greet.js:

import greetings from './data.js';
export function sayHello() {
  return greetings.hello;
}

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

تحميل بيانات العاملين تلقائيًا باستخدام "modulepreload"

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

<!-- preloads worker.js and its dependencies: -->
<link rel="modulepreload" href="worker.js">

<script>
  addEventListener('load', () => {
    // our worker code is likely already parsed and ready to execute!
    const worker = new Worker('worker.js', { type: 'module' });
  });
</script>

يمكن أيضًا استخدام الوحدات المُحمَّلة مسبقًا من قِبل كل من العاملين في سلسلة التعليمات الرئيسية والوحدات. هذا مفيد التي يتم استيرادها في كلا السياقين، أو في الحالات التي لا يمكن معرفتها مسبقًا ما إذا كان سيتم استخدام وحدة في سلسلة التعليمات الرئيسية أو في عامل.

في السابق، كانت الخيارات المتاحة للتحميل المُسبق للنصوص البرمجية للعاملين على الويب محدودة ولم تكن موثوقة بالضرورة. كان للعمال الكلاسيكيين "عامل" خاص بهم نوع مورد التحميل المسبق، ولكن لا تم تنفيذ <link rel="preload" as="worker">. ونتيجة لذلك، تم تحديد التقنية الأساسية متاحة للتحميل المُسبق للعاملين على الويب هي استخدام <link rel="prefetch">، الذي اعتمد كليًا على ذاكرة التخزين المؤقت لـ HTTP. وعند استخدامه مع عناوين التخزين المؤقت الصحيحة، يصبح من الممكن لتفادي اضطرار العامل إلى الانتظار لتنزيل البرنامج النصي الخاص بالعامل. ومع ذلك، وعلى عكس modulepreload لم تتوافق هذه التقنية مع اعتماديات التحميل المُسبق أو التحليل المسبق.

ماذا عن العمّال المشتركين؟

الموظفون المشترَكون بالاعتماد على وحدات JavaScript بدءًا من الإصدار Chrome 83. مثل العاملين المخلصين، يؤدي إنشاء عامل مشترك باستخدام الخيار {type:"module"} الآن إلى تحميل النص البرمجي الخاص بالعامل بدلاً من استخدام برنامج نصي كلاسيكي:

const worker = new SharedWorker('/worker.js', {
  type: 'module'
});

قبل إتاحة وحدات JavaScript، كانت الدالة الإنشائية SharedWorker() تتوقع عنوان URL ووسيطة name اختيارية. سيبقى هذا بالإمكان استخدام العمّال المشترَك الكلاسيكي، لكن يتطلب إنشاء العاملين المشتركين في الوحدة استخدام وسيطة options الجديدة. دالة المتاحة الخيارات هي نفسها تلك الخاصة بالعامل المتخصّص، بما في ذلك الخيار name الذي يحل محل وسيطة name السابقة.

ماذا عن مشغّل الخدمات؟

سبق أن تم ضبط مواصفات مشغّل الخدمات تم تحديثها لإتاحة قبول وحدة JavaScript كنقطة دخول باستخدام خيار {type:"module"} نفسه الذي يستخدمه العاملون في الوحدة، ولكن لم يتم تنفيذ هذا التغيير في المتصفحات بعد. بمجرد حدوث ذلك، سيكون من الممكن لإنشاء مثيل عامل خدمة باستخدام وحدة JavaScript باستخدام الرمز التالي:

navigator.serviceWorker.register('/sw.js', {
  type: 'module'
});

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

موارد إضافية وقراءة إضافية