جدولة JavaScript أفضل باستخدام isInputPending()

واجهة برمجة تطبيقات JavaScript جديدة قد تساعدك في تجنُّب المفاضلة بين أداء التحميل واستجابة الإدخال.

Nate Schloss
Nate Schloss
Andrew Comminos
Andrew Comminos

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

وللتخلص من الحاجة إلى إجراء هذه المفاضلة، اقترحت Facebook ونفذتها isInputPending() API في Chromium لتحسين سرعة الاستجابة بدون العائد. استنادًا إلى الملاحظات والآراء بشأن مرحلة التجربة والتقييم، أجرينا عددًا من التعديلات على ويسرّنا الإعلان عن أنّ واجهة برمجة التطبيقات أصبحت تشحن بشكل تلقائي في Chromium. 87!

توافُق المتصفح

دعم المتصفح

  • Chrome: 87.
  • الحافة: 87.
  • Firefox: غير مدعوم.
  • Safari: غير متاح.

المصدر

تم شحن isInputPending() في المتصفّحات المستندة إلى Chromium بدءًا من الإصدار 87. لم يُشِر أي متصفّح آخر إلى نية شحن واجهة برمجة التطبيقات.

الخلفية

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

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

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

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

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

مخطّط بياني يوضّح أنّ isInputPending() يتيح لـ JS التحقّق مما إذا كان هناك إدخال من المستخدم في انتظار المراجعة، بدون إرجاع عملية التنفيذ بالكامل إلى المتصفّح.

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

لقد تلقّينا الآن ملاحظات من مرحلة التجربة والتقييم ومن المشاركين الآخرين في مجموعة عمل W3C Web Performance Group ونفذت تغييرات على واجهة برمجة التطبيقات.

مثال: أداة جدولة أكثر إنتاجية

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

const DEADLINE = performance.now() + QUANTUM;
while (workQueue.length > 0) {
  if (performance.now() >= DEADLINE) {
    // Yield the event loop if we're out of time.
    setTimeout(processWorkQueue);
    return;
  }
  let job = workQueue.shift();
  job.execute();
}

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

لا بأس بذلك، ولكن هل يمكننا تحسينه؟ بكل تأكيد.

const DEADLINE = performance.now() + QUANTUM;
while (workQueue.length > 0) {
  if (navigator.scheduling.isInputPending() || performance.now() >= DEADLINE) {
    // Yield if we have to handle an input event, or we're out of time.
    setTimeout(processWorkQueue);
    return;
  }
  let job = workQueue.shift();
  job.execute();
}

من خلال إجراء مكالمة إلى navigator.scheduling.isInputPending()، يمكننا: الاستجابة بشكل أسرع للإدخالات مع الاستمرار في ضمان عمل حظر العرض وتنفيذه بلا انقطاع. في حال عدم اهتمامنا بمعالجة أي مشكلة بخلاف الإدخال (مثل الطلاء) حتى يكتمل العمل، يمكننا بسهولة زيادة بطول QUANTUM أيضًا.

الإعداد "مستمر" تلقائيًا لا يتم عرض الأحداث من isInputPending(). هذه تتضمّن mousemove وpointermove وغير ذلك. إذا كنت مهتمًا بإعطاء هذه أيضًا، فلا توجد مشكلة. من خلال تقديم كائن إلى isInputPending() مع تم ضبط includeContinuous على true، حان الوقت للانطلاق:

const DEADLINE = performance.now() + QUANTUM;
const options = { includeContinuous: true };
while (workQueue.length > 0) {
  if (navigator.scheduling.isInputPending(options) || performance.now() >= DEADLINE) {
    // Yield if we have to handle an input event (any of them!), or we're out of time.
    setTimeout(processWorkQueue);
    return;
  }
  let job = workQueue.shift();
  job.execute();
}

هذا كل شيء! تدمج أطر العمل مثل React دعم isInputPending() في ومكتبات الجدولة الأساسية باستخدام منطق مماثل. نأمل أن يؤدي هذا مطوِّرو البرامج الذين يستخدمون أُطر العمل هذه للاستفادة من isInputPending() وراء الكواليس بدون إعادة صياغته بشكل ملحوظ.

تحقيق الأرباح ليس سيئًا دائمًا

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

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

انتبه أيضًا للصفحات الأخرى التي تشترك في حلقة أحداث. على أنظمة أساسية مثل ففي Chrome لنظام Android، من الشائع جدًا أن تتشارك عدة أصول في الأحداث تكرار. لن تعرض isInputPending() مطلقًا true إذا تم إرسال الإدخال إلى إطار من مصادر متعددة، وبالتالي قد تتداخل الصفحات الخلفية مع مدى استجابة الصفحات الأمامية. قد ترغب في تقليل الأرباح أو تأجيلها عند العمل في الخلفية باستخدام Page visibility API.

ننصحك باستخدام isInputPending() بتقديرك. إذا لم تكن هناك إجراء حظر المستخدم، ثم التعامل مع الآخرين في حلقة الحدث عن طريق عائدة بشكل متكرر. قد تكون المهام الطويلة ضارة.

ملاحظات

  • اترك ملاحظات حول المواصفات في is-input-Pending.
  • يمكنك التواصل مع @acomminos (أحد مؤلفي المواصفات). على Twitter.

الخاتمة

يسعدنا إطلاق isInputPending() وقدرة مطوّري البرامج على على البدء في استخدامه اليوم. واجهة برمجة التطبيقات هذه هي أول مرة يُنشئ فيها Facebook واجهة برمجة تطبيقات الويب الجديدة ونقلتها من حضانة الأفكار إلى اقتراح المعايير إلى الشحن في المتصفح. نود أن نشكر كل من ساعدنا في تحقيق هذا الهدف ونقدِّم له تحية خاصة لكل من ساعدنا في تطوير Chrome هذه الفكرة وشحنها!

صورة رئيسية من إعداد Will H McMahan على إزالة البداية