الحدث المستمعين وعمال الويب

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



لحظة فهمت كل شيء


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

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

فيما يلي مثال على الكود الذي يمكنك تذكره هنا:

const calculateResultsButton = document.getElementById('calculateResultsButton'); const openMenuButton = document.getElementById('#openMenuButton'); const resultBox = document.getElementById('resultBox'); calculateResultsButton.addEventListener('click', (e) => {     // "    -, ,   ,     //   DOM   ?"     const result = performLongRunningCalculation();     resultBox.innerText = result; }); openMenuButton.addEventListener('click', (e) => {     //      . }); 

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

كيفية "إبطاء" الصفحة


هنا هو مشروع CodePen يدل على ما سبق.


مشروع يوضح الموقف الذي تكون فيه الصفحات بطيئة

يؤدي الضغط على زر Freeze إلى بدء التطبيق في حل المهمة المتزامنة. كل هذا يستغرق 3 ثوان (يحاكي أداء العمليات الحسابية المطولة). إذا قمت في نفس الوقت بالنقر فوق الزر " Increment ، ثم حتى 3 ثوانٍ ، فلن يتم تحديث القيمة في حقل " Click Count . سيتم كتابة قيمة جديدة مقابلة لعدد النقرات على Increment إلى هذا الحقل فقط بعد مرور ثلاث ثوانٍ. يتم حظر الدفق الرئيسي أثناء توقف مؤقت. نتيجة لذلك ، يبدو كل شيء في نافذة التطبيق غير صالح. يتم تجميد واجهة التطبيق. الأحداث الناشئة في عملية "التجميد" تنتظر الفرصة لاستخدام موارد التيار الرئيسي.

إذا قمت بالنقر فوق " Freeze وحاول resize me! ، ثم ، مرة أخرى ، حتى يتم مرور ثلاث ثوانٍ ، لن يتغير حجم الحقل. وبعد ذلك ، سيتغير حجم الحقل ، ولكن ليست هناك حاجة للحديث عن أي "نعومة" في الواجهة.

مستمعو الأحداث ظاهرة أكبر بكثير مما قد يبدو للوهلة الأولى


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

 Array  .from([document, ...document.querySelectorAll('*')])  .reduce((accumulator, node) => {    let listeners = getEventListeners(node);    for (let property in listeners) {      accumulator = accumulator + listeners[property].length    }    return accumulator;  }, 0); 

قمت بتشغيل هذا البرنامج النصي على صفحات مختلفة واكتشفت عدد المستمعين من الأحداث الذين يستخدمونهم. يتم عرض نتائج تجربتي في الجدول التالي.
تطبيق
عدد مستمعي الحدث
دروببوإكس
602
رسائل جوجل
581
رديت
692
يوتيوب
6054 (!!!)

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

تخلص من "الفرامل" مع عمال الشبكة


بالنظر إلى كل ما سبق ، دعنا نعيد كتابة المثال السابق. هنا هو نسخته الجديدة. يبدو تمامًا مثل القديم ، لكن في الداخل يتم ترتيبه بشكل مختلف. وهي الآن العملية التي كانت تستخدم لمنع الخيط الرئيسي قد تم نقلها إلى الخيط الخاص بها. إذا فعلت نفس الشيء مع هذا المثال كما في المثال السابق ، يمكنك ملاحظة اختلافات إيجابية خطيرة. وهي أنه بعد النقر فوق الزر " Freeze انقر فوق " Increment ، وسيتم تحديث حقل " Click Count (بعد انتهاء عامل الويب ، على أي حال ، سيتم إضافة الرقم 1 إلى قيمة " Click Count ). الشيء نفسه ينطبق على resize me! . لا يعمل التعليمات البرمجية قيد التشغيل في مؤشر ترابط منفصل على حظر مستمعي الأحداث. يتيح ذلك لجميع عناصر الصفحة الاستمرار في العمل حتى أثناء تنفيذ العملية التي كانت "تجمد" الصفحة سابقًا.

فيما يلي رمز JS لهذا المثال:

 const button1 = document.getElementById('button1'); const button2 = document.getElementById('button2'); const count = document.getElementById('count'); const workerScript = `  function pause(ms) {    let time = new Date();    while ((new Date()) - time <= ms) {}               }  self.onmessage = function(e) {    pause(e.data);    self.postMessage('Process complete!');  } `; const blob = new Blob([  workerScript, ], {type: "text/javascipt"}); const worker = new Worker(window.URL.createObjectURL(blob)); const bumpCount = () => {  count.innerText = Number(count.innerText) + 1; } worker.onmessage = function(e) {  console.log(e.data);  bumpCount(); } button1.addEventListener('click', async function () {  worker.postMessage(3000); }); button2.addEventListener('click', function () {  bumpCount(); }); 

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

  • Workerize - يسمح لك بتشغيل الوحدات النمطية في عمال الويب.
  • Greenlet - يجعل من الممكن تنفيذ أجزاء عشوائية من التعليمات البرمجية غير المتزامنة في العاملين على شبكة الإنترنت.
  • Comlink - يوفر طبقة ملائمة من التجريد عبر API Workers Web.

النتائج


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

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

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

أعزائي القراء! هل تستخدم عمال الويب في مشاريعك؟


Source: https://habr.com/ru/post/ar479268/


All Articles