كان إنشاء مواقع إلكترونية تستجيب بسرعة للبيانات التي يُدخلها المستخدمون من بين الجوانب الأكثر تحديًا في أداء الويب، وهو ما يعمل فريق Chrome جاهدًا على مساعدته في تحقيقه. في هذا العام فقط، تم الإعلان عن أنّ مقياس مدى استجابة الصفحة لتفاعلات المستخدم (INP) سينتقل من الحالة التجريبية إلى الحالة المعلّقة. ومن المقرر أن يحلّ محلّ مهلة الاستجابة الأولى (FID) كأحد مؤشرات أداء الويب الأساسية في آذار (مارس) 2024.
في إطار الجهود المتواصلة لتقديم واجهات برمجة تطبيقات جديدة تساعد مطوّري الويب في جعل مواقعهم الإلكترونية سريعة قدر الإمكان، يجري فريق Chrome حاليًا تجربة أولى لscheduler.yield
بدءًا من الإصدار 115 من Chrome. scheduler.yield
هي إضافة جديدة مقترَحة لواجهة برمجة التطبيقات الخاصة بجدول التشغيل تتيح طريقة أسهل وأفضل لإعادة التحكّم إلى سلسلة المهام الرئيسية مقارنةً بالطُرق التي كان يُعتمَد عليها تقليديًا.
عند التسليم
تستخدِم JavaScript نموذج التشغيل إلى الاكتمال للتعامل مع المهام. وهذا يعني أنّه عند تنفيذ مهمة في سلسلة المحادثات الرئيسية، يتم تنفيذ هذه المهمة طوال المدة اللازمة لإكمالها. عند اكتمال إحدى المهام، يتم تسليم التحكّم إلى سلسلة المحادثات الرئيسية، ما يسمح لسلسلة المحادثات الرئيسية بمعالجة المهمة التالية في قائمة الانتظار.
باستثناء الحالات القصوى التي لا تنتهي فيها المهمة أبدًا، مثل حلقة لا نهائية، فإنّ التوقّف المؤقت هو جانب لا مفر منه من منطق جدولة المهام في JavaScript. سيحدث ذلك، ولكن السؤال هو متى، والأفضل أن يحدث ذلك في أقرب وقت ممكن. عندما تستغرق المهام وقتًا طويلاً جدًا لتنفيذها، أي أكثر من 50 ملي ثانية على وجه التحديد، تُعتبر مهامًا طويلة.
إنّ المهام الطويلة هي مصدر ضعف استجابة الصفحة، لأنّها تؤخّر قدرة المتصفّح على الاستجابة لبيانات المستخدم. وكلما زادت وتيرة تنفيذ المهام الطويلة وكلما طالت مدّتها، زاد احتمال أن يعتقد المستخدمون أنّ الصفحة بطيئة أو حتى أنّها معطّلة.
ومع ذلك، لا يعني بدء الرمز البرمجي لمهمة في المتصفّح أنّ عليك الانتظار إلى أن تنتهي هذه المهمة قبل إعادة التحكّم إلى سلسلة التعليمات الرئيسية. يمكنك تحسين الاستجابة لإدخال المستخدم على صفحة من خلال الاستسلام صراحةً في مهمة، ما يؤدي إلى تقسيم المهمة لإكمالها في الفرصة المتاحة التالية. ويسمح ذلك للمهام الأخرى بالحصول على وقت في سلسلة التعليمات الرئيسية في وقت أقرب مما لو كان عليها الانتظار حتى تكتمل المهام الطويلة.
عند التنازل عن المعالجة صراحةً، يعني ذلك أنّك تخبر المتصفّح بأنّك "أدرك أنّ العمل الذي سأُجريه قد يستغرق بعض الوقت، ولا أريد أن تضطر إلى تنفيذ كل هذا العمل قبل الاستجابة لإدخال المستخدم أو المهام الأخرى التي قد تكون مهمة أيضًا". وهي أداة قيّمة في مجموعة أدوات المطوّر يمكن أن تساهم بشكل كبير في تحسين تجربة المستخدم.
مشكلة استراتيجيات تحقيق الأرباح الحالية
إنّ الطريقة الشائعة لعرض تستخدِم setTimeout
مع قيمة مهلة تبلغ 0
. يعمل هذا الإجراء لأنّ دالة الاستدعاء التي تم تمريرها إلى setTimeout
ستنقل العمل المتبقّي إلى مهمة منفصلة ستتم إضافتها إلى "قائمة الانتظار" لتنفيذها لاحقًا. بدلاً من الانتظار إلى أن يُجري المتصفّح ذلك من تلقاء نفسه، يمكنك تقسيم هذا الجزء الكبير من العمل إلى أجزاء أصغر.
ومع ذلك، قد يؤدي استخدام setTimeout
إلى حدوث تأثير جانبي غير مرغوب فيه: سيتم نقل العمل الذي يأتي بعد نقطة الاستسلام إلى نهاية قائمة المهام. ستظل المهام التي تم تحديد موعدها من خلال تفاعلات المستخدمين تنتقل إلى مقدمة قائمة الانتظار على النحو المطلوب، ولكن قد يؤدي العمل المتبقي الذي أردت تنفيذه بعد التنازل عن الموارد صراحةً إلى تأخيره أكثر بسبب المهام الأخرى من المصادر المتنافسة التي تم وضعها في قائمة الانتظار قبله.
للاطّلاع على هذا الإجراء، جرِّب العرض التجريبي لتطبيق Glitch أو جرِّبه في الإصدار المضمّن أدناه. يتألّف العرض الترويجي من بضعة أزرار يمكنك النقر عليها، ومربّع أسفل هذه الأزرار يسجّل وقت تنفيذ المهام. عند الانتقال إلى الصفحة، اتّبِع الخطوات التالية:
- انقر على الزر العلوي الذي يحمل العنوان تنفيذ المهام بشكل دوري، ما سيؤدي إلى جدولة المهام الحظرية لتشغيلها من حين لآخر. عند النقر على هذا الزر، سيتم ملء سجلّ المهام بعدة رسائل نصها تم تنفيذ مهمة الحظر باستخدام
setInterval
. - بعد ذلك، انقر على الزرّ تشغيل حلقة، مع عرض
setTimeout
في كل تكرار.
ستلاحظ أنّ المربّع في أسفل العرض التوضيحي سيعرض رسالة مماثلة لما يلي:
Processing loop item 1
Processing loop item 2
Ran blocking task via setInterval
Processing loop item 3
Ran blocking task via setInterval
Processing loop item 4
Ran blocking task via setInterval
Processing loop item 5
Ran blocking task via setInterval
Ran blocking task via setInterval
يوضّح هذا الإخراج سلوك "نهاية قائمة انتظار المهام" الذي يحدث عند التوقّف باستخدام setTimeout
. تعالج الحلقة التي يتم تشغيلها خمسة عناصر، وتُعرِض setTimeout
بعد معالجة كل عنصر.
يوضّح ذلك مشكلة شائعة على الويب: من الشائع أن يسجِّل نص برمجي، خاصةً نص برمجي تابع لجهة خارجية، وظيفة موقّت تُنفِّذ العمل على فترات زمنية معيّنة. إنّ سلوك "نهاية قائمة انتظار المهام" الذي يأتي مع التوقّف باستخدام setTimeout
يعني أنّه قد يتم وضع العمل من مصادر المهام الأخرى في قائمة الانتظار قبل العمل المتبقّي الذي يجب أن تنفّذه الحلقة بعد التوقّف.
قد تكون هذه النتيجة مرغوبًا فيها أو لا، وذلك حسب تطبيقك، ولكن في كثير من الحالات، قد يشعر المطوّرون بالتردد في التخلي عن التحكّم في سلسلة المهام الرئيسية بسهولة. من الجيد التنازل عن الوقت لأنّ تفاعلات المستخدمين تحصل على فرصة للتنفيذ في وقت أقرب، ولكنّها تسمح أيضًا بحصول المهام الأخرى غير المرتبطة بالتفاعل مع المستخدمين على وقت في سلسلة المهام الرئيسية أيضًا. هذه مشكلة حقيقية، ولكن يمكن لفريق scheduler.yield
مساعدتك في حلّها.
إدخال scheduler.yield
كانت ميزة scheduler.yield
متاحة كميزة تجريبية لمنصّة الويب منذ الإصدار 115 من Chrome. قد يخطر لك سؤال: "لماذا أحتاج إلى دالة خاصة لعرض النتائج عندما تؤدي دالة setTimeout
ذلك؟"
تجدر الإشارة إلى أنّ الاستسلام لم يكن هدفًا من أهداف تصميم setTimeout
، بل كان تأثيرًا جانبيًا لطيفًا في جدولة طلب إعادة الاتصال لتنفيذه في وقت لاحق في المستقبل، حتى مع تحديد قيمة مهلة 0
. من المهم تذكُّر أنّ استخدام الرمز setTimeout
يؤدي إلى إرسال العمل المتبقّي إلى الخلف في قائمة انتظار المهام. يُرسِل scheduler.yield
تلقائيًا العمل المتبقّي إلى المقدمة من قائمة الانتظار. وهذا يعني أنّ العمل الذي أردت استئنافه فورًا بعد التوقّف لن يتراجع إلى الخلف مقابل المهام الواردة من مصادر أخرى (باستثناء التفاعلات مع المستخدمين).
scheduler.yield
هي دالة تُسلّم التحكم إلى سلسلة المحادثات الرئيسية وتُعرِض Promise
عند استدعائها. وهذا يعني أنّه يمكنك await
في دالة async
:
async function yieldy () {
// Do some work...
// ...
// Yield!
await scheduler.yield();
// Do some more work...
// ...
}
للاطّلاع على scheduler.yield
قيد التنفيذ، اتّبِع الخطوات التالية:
- انتقِل إلى
chrome://flags
. - فعِّل تجربة ميزات قاعدة الويب التجريبية. قد تحتاج إلى إعادة تشغيل Chrome بعد إجراء ذلك.
- انتقِل إلى الصفحة التجريبية أو استخدِم النسخة المضمّنة منها أسفل هذه القائمة.
- انقر على الزر العلوي الذي يحمل العنوان تنفيذ المهام بشكل دوري.
- أخيرًا، انقر على الزر تشغيل حلقة مع عرض
scheduler.yield
في كل تكرار.
ستظهر النتيجة في المربّع في أسفل الصفحة على النحو التالي:
Processing loop item 1
Processing loop item 2
Processing loop item 3
Processing loop item 4
Processing loop item 5
Ran blocking task via setInterval
Ran blocking task via setInterval
Ran blocking task via setInterval
Ran blocking task via setInterval
Ran blocking task via setInterval
على عكس التسجيل التجريبي الذي يُعطِل باستخدام setTimeout
، يمكنك ملاحظة أنّ الحلقة، على الرغم من أنّها تُعطِل بعد كل تكرار، لا تُرسِل العمل المتبقّي إلى نهاية "قائمة الانتظار"، بل إلى مقدمتها. يمنحك ذلك أفضل ما في الحالتَين: يمكنك استخدام ميزة "التخلي عن المساحة" لتحسين استجابة الإدخال على موقعك الإلكتروني، ولكن يمكنك أيضًا التأكّد من عدم تأخّر العمل الذي أردت إكماله بعد استخدام هذه الميزة.
ننصحك بتجربة ذلك.
إذا كانت ميزة scheduler.yield
تبدو مثيرة للاهتمام وتريد تجربتها، يمكنك إجراء ذلك بطريقتَين بدءًا من الإصدار 115 من Chrome:
- إذا كنت تريد تجربة
scheduler.yield
على الجهاز، اكتبchrome://flags
وأدخِله في شريط عناوين Chrome، ثم اختَر تفعيل من القائمة المنسدلة في قسم ميزات منصة الويب التجريبية. سيؤدي ذلك إلى إتاحةscheduler.yield
(وأي ميزات تجريبية أخرى) في نسختك من Chrome فقط. - إذا كنت تريد تفعيل
scheduler.yield
لمستخدمي Chromium الحقيقيين على مصدر يمكن للجميع الوصول إليه، عليك الاشتراك في مرحلة تقييم وتجربةscheduler.yield
. يتيح لك ذلك تجربة الميزات المقترَحة بأمان لفترة زمنية معيّنة، كما يقدّم لفريق Chrome إحصاءات قيّمة حول كيفية استخدام هذه الميزات في المجال. لمزيد من المعلومات حول آلية عمل الفترات التجريبية للإصدارات الأصلية، يُرجى قراءة هذا الدليل.
تعتمد طريقة استخدام scheduler.yield
، مع مواصلة إتاحة المتصفحات التي لا تطبّقه، على أهدافك. يمكنك استخدام المكتبة الرسمية لإضافة وظائف إلى الويب. يكون الpolyfill مفيدًا إذا كان ما يلي ينطبق على حالتك:
- أنت تستخدم حاليًا
scheduler.postTask
في تطبيقك لجدولة المهام. - إذا كنت تريد تحديد أولويات المهام والأرباح
- تريد أن تتمكّن من إلغاء المهام أو إعادة تحديد أولوياتها من خلال فئة
TaskController
التي تقدّمها واجهة برمجة التطبيقاتscheduler.postTask
.
إذا لم يكن هذا الوصف يناسب حالتك، قد لا يكون ملف الpolyfill مناسبًا لك. في هذه الحالة، يمكنك طرح الإصدار الاحتياطي بطريقتَين. تستخدِم الطريقة الأولى الرمز scheduler.yield
إذا كان متاحًا، ولكن تعود إلى الرمز setTimeout
إذا لم يكن متاحًا:
// A function for shimming scheduler.yield and setTimeout:
function yieldToMain () {
// Use scheduler.yield if it exists:
if ('scheduler' in window && 'yield' in scheduler) {
return scheduler.yield();
}
// Fall back to setTimeout:
return new Promise(resolve => {
setTimeout(resolve, 0);
});
}
// Example usage:
async function doWork () {
// Do some work:
// ...
await yieldToMain();
// Do some other work:
// ...
}
يمكن أن تنجح هذه الطريقة، ولكن كما يمكنك توقّع ذلك، لن تؤدي المتصفّحات التي لا تتوافق مع scheduler.yield
إلى ظهور إعلاناتك في "مقدّمة الطابور". إذا كنت تفضّل عدم تحقيق أي عائد على الإطلاق، يمكنك تجربة نهج آخر يستخدم scheduler.yield
إذا كان متاحًا، ولكن لن يحقّق أي عائد على الإطلاق في حال عدم توفّره:
// A function for shimming scheduler.yield with no fallback:
function yieldToMain () {
// Use scheduler.yield if it exists:
if ('scheduler' in window && 'yield' in scheduler) {
return scheduler.yield();
}
// Fall back to nothing:
return;
}
// Example usage:
async function doWork () {
// Do some work:
// ...
await yieldToMain();
// Do some other work:
// ...
}
scheduler.yield
هي إضافة مثيرة إلى واجهة برمجة التطبيقات الخاصة بجدول التشغيل، ومن المرجّح أن تسهّل على المطوّرين تحسين وقت الاستجابة مقارنةً باستراتيجيات تحقيق الأرباح الحالية. إذا كانت scheduler.yield
تبدو لك واجهة برمجة تطبيقات مفيدة، يُرجى المشاركة في أبحاثنا للمساعدة في تحسينها وتقديم ملاحظات حول كيفية تحسينها بشكل أكبر.
الصورة الرئيسية من Unsplash، لأحد أعمال Jonathan Allison.