العاملون على الويب في العمل: لماذا هم مفيدون ، وكيف يجب أن تستخدمها

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

فمثلا:

average = (numbers) => { let startTime = new Date().getTime(); let len = numbers, sum = 0, i; if (len === 0) { return 0; } for (i = 0; i  { alert("Hello World !!"); } /* Paste the above code in browser dev tool console and try to call average(10000) and hello one by one */

في المثال أعلاه ، إذا اتصلت بالمتوسط قبل طريقة hello ، فستصبح صفحتك غير مستجيبة ولن تتمكن من النقر فوق Hello حتى يكتمل تنفيذ المتوسط .

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

البرمجة غير المتزامنة

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

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

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

فمثلا:

average = (numbers) => { let startTime = new Date().getTime(); var len = numbers, sum = 0, i; if (len === 0) { return 0; } let calculateSumAsync = (i) => { if (i  { sum += i; calculateSumAsync(i + 1); }, 0); } else { // The end of the array is reached so we're invoking the alert. let endTime = new Date().getTime(); alert('Average - ', sum / len); } }; calculateSumAsync(0); }; hello = () => { alert('Hello World !!') };

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

هنا ، لنفس الإدخال 10000 ، استغرق الأمر حوالي 60 ثانية ، وهو أمر غير فعال للغاية.

إذن ، كيف يمكننا حل هذه الأنواع من المشكلات بكفاءة؟

الجواب هو عمال الويب.

من هم عمال الويب؟

يعد العاملون على الويب في Javascript طريقة رائعة لتنفيذ بعض المهام الشاقة للغاية والوقت الذي يستغرقه في سلسلة منفصلة عن السلسلة الرئيسية. تعمل في الخلفية وتؤدي المهام دون التدخل في واجهة المستخدم.

عمال الويب ليسوا جزءًا من JavaScript ، بل هم ميزة متصفح يمكن الوصول إليها من خلال JavaScript.

يتم إنشاء عمال الويب بواسطة دالة منشئ Worker () التي تقوم بتشغيل ملف JS مسمى.

// create a dedicated web worker const myWorker = new Worker('worker.js');

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

سوف نتعلم المزيد عن إنشاء وعمل العاملين على الويب في القسم التالي.

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

هناك بعض القيود مع العاملين على الويب -

  1. لا يمكنك معالجة DOM مباشرة من داخل العامل.
  2. لا يمكنك استخدام بعض الأساليب والخصائص الافتراضية لكائن النافذة لأن كائن النافذة غير متاح داخل مؤشر ترابط عامل.
  3. يمكن الوصول إلى السياق داخل مؤشر ترابط العامل عبر DedicatedWorkerGlobalScope أو SharedWorkerGlobalScope اعتمادًا على الاستخدام.

ميزات عمال الويب

هناك نوعان من العاملين على الويب -

  1. عامل ويب مخصص - لا يمكن الوصول إلى العامل المخصص إلا من خلال البرنامج النصي الذي يطلق عليه.
  2. عامل الويب المشترك - يمكن الوصول إلى عامل مشترك من خلال نصوص متعددة - حتى إذا تم الوصول إليها من خلال نوافذ مختلفة أو إطارات مضمنة أو حتى عمال.

دعونا نناقش المزيد حول هذين النوعين من العاملين على الويب -

إنشاء عامل الويب

الإنشاء هو نفسه إلى حد كبير لكل من عامل الويب المخصص والمشترك.

عامل ويب مخصص

  • يعد إنشاء عامل جديد أمرًا بسيطًا ، ما عليك سوى الاتصال بالمُنشئ العامل ومرر مسار البرنامج النصي الذي تريد تنفيذه كعامل.
// create a dedicated web worker const myWorker = new Worker('worker.js');

عامل الويب المشترك:

  • إن إنشاء عامل مشترك جديد يشبه إلى حد كبير عمل العامل المتفاني ، ولكن باسم مُنشئ مختلف.
// creating a shared web worker const mySharedWorker = new SharedWorker('worker.js');

التواصل بين الخيط الرئيسي والعامل

التواصل بين موضوع الرئيسي وترابط يحدث عبر postMessage طريقة و onmessage معالج الحدث.

عامل ويب مخصص

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

(() => { // new worker let myWorker = new Worker('worker.js'); // event handler to recieve message from worker myWorker.onmessage = (e) => { document.getElementById('time').innerHTML = `${e.data.time} seconds`; }; let average = (numbers) => { // sending message to web worker with an argument myWorker.postMessage(numbers); } average(1000); })();

وداخل عامل الويب ، يمكنك الرد عند تلقي الرسالة عن طريق كتابة كتلة معالج حدث مثل هذا:

onmessage = (e) => { let numbers = e.data; let startTime = new Date().getTime(); let len = numbers, sum = 0, i; if (len === 0) { return 0; } for (i = 0; i < len; i++) { sum += i; } let endTime = new Date().getTime(); postMessage({average: sum / len, time: ((endTime - startTime) / 1000)}) };

في onmessageمعالج يسمح لتشغيل بعض التعليمات البرمجية كلما تم تلقي رسالة.

نحن هنا نحسب متوسط ​​الأرقام ثم نستخدمها postMessage()مرة أخرى لإعادة النتيجة إلى الموضوع الرئيسي.

كما ترى في السطر 6 في main.js ، استخدمنا حدث onmessage في مثيل العامل. لذلك كلما استخدم مؤشر ترابط العامل postMessage ، يتم تشغيل onmessage في مؤشر الترابط الرئيسي.

  • عامل الويب المشترك

    In case of a shared web worker, communication system is little different. As one worker is shared between multiple scripts, we need to communicate via the port object of worker instance. This is done implicitly in case of dedicated workers. You need to use postMessage method whenever you want to send message to the worker.

(() => { // new worker let myWorker = new Worker('worker.js'); // event handler to recieve message from worker myWorker.onmessage = (e) => { document.getElementById('time').innerHTML = `${e.data.time} seconds`; }; let average = (numbers) => { // sending message to web worker with an argument myWorker.postMessage(numbers); } average(1000);

Inside a web worker (main-shared-worker.js) it is a little complex. First, we use an onconnect handler to fire code when a connection to the port happens (line 2).

We use the ports attribute of this event object to grab the port and store it in a variable (line 4).

Next, we add a message handler on the port to do the calculation and return the result to the main thread (line 7 and line 25) like this:

onmessage = (e) => { let numbers = e.data; let startTime = new Date().getTime(); let len = numbers, sum = 0, i; if (len === 0) { return 0; } for (i = 0; i < len; i++) { sum += i; } let endTime = new Date().getTime(); postMessage({average: sum / len, time: ((endTime - startTime) / 1000)}) };

Termination of a web worker

If you need to immediately terminate a running worker from the main thread, you can do so by calling the worker’s terminate method:

// terminating a web worker instance myWorker.terminate();

The worker thread is killed immediately without an opportunity to complete its operations.

Spawning of web worker

Workers may spawn more workers if they wish. But they must be hosted within the same origin as the parent page.

Importing Scripts

Worker threads have access to a global function, importScripts(), which lets them import scripts.

importScripts(); /* imports nothing */ importScripts('foo.js'); /* imports just "foo.js" */ importScripts('foo.js', 'bar.js'); /* imports two scripts */ importScripts('//example.com/hello.js'); /* You can import scripts from other origins */

Working Demo

We have discussed some of the approaches above to achieve async programming so that our UI doesn’t get blocked due to any heavy computational task. But there are some limitations to those approaches. So we can use web workers to solve these kind of problems efficiently.

Click here to run this live demo.

Here, you will see 3 sections:

  1. Blocking Code:

    When you click on calculate average, the loader does not display and after some time you see the final result and time taken. This is because as soon as the average method gets called, I have triggered the showLoader method also. But since JS is single threaded, it won’t execute showLoader until the execution of average gets completed. So, you won’t be able to see the loader in this case ever.

  2. Async Code:

    In this I tried to achieve the same functionality by using the setTimeout method and putting every function execution into an event loop. You will see the loader in this case, but the response takes time as compared to the method defined above.

  3. Web worker:

    This is an example of using a web worker. In this you will see the loader as soon as you click on calculate average and you will get a response in the same time as of method 1, for the same number.

You can access the source code for the same here.

Advanced concepts

There are some advanced concepts related to web workers. We won’t be discussing them in detail, but its good to know about them.

  1. Content Security Policy —

    Web workers have their own execution context independent of the document that created them and because of this reason they are not governed by the Content Security Policy of the parent thread/worker.

    The exception to this is if the worker script's origin is a globally unique identifier (for example, if its URL has a scheme of data or blob). In this case, the worker inherit the content security policy of the document or worker that created it.

  2. Transferring data to and from workers

    Data passed between main and worker thread is copied and not shared. Objects are serialized as they're handed to the worker, and subsequently, de-serialized on the other end. The page and worker do not share the same instance, so the end result is that a duplicate is created on each end.

    Browsers implemented Structured Cloning algorithm to achieve this.

  3. Embedded workers —

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

    embedded worker   // This script WON'T be parsed by JS engines because its MIME type is text/js-worker. var myVar = 'Hello World!'; // worker block function onmessage(e) { // worker code }    

هناك الكثير من حالات الاستخدام لاستخدام العاملين على الويب في تطبيقنا. لقد ناقشت للتو سيناريو صغير. آمل أن يساعدك هذا على فهم مفهوم العاملين على الويب.

[روابط]

Github Repo: //github.com/bhushangoel/webworker-demo-1 عامل الويب أثناء العمل: //bhushangoel.github.io/webworker-demo-1/JS demo showcase: //bhushangoel.github.io/

شكرا لقرائتك.

تعلم سعيد :)

نُشر في الأصل على www.thehungrybrain.com.