مستوى المقال: متوسط
لو الـ dashboard بيستجيب للكليك بعد 320 مللي ثانية، والمستخدم بيدوس مرتين ظناً منه إن الزرار اتعطل، الموقع مش بطيء — هو شغّال long task بيقفل الـ main thread. مقياس INP بقى رسمي في Core Web Vitals من 12 مارس 2024 بدل FID القديم، و Google بتحسبه ضمن إشارات ترتيب الموقع على Search.
INP و scheduler.yield: نزّل زمن استجابة الكليك من 320ms لـ 80ms في 2026
المشكلة باختصار
المتصفح بيشغّل JavaScript كله على thread واحد اسمه الـ main thread. الـ thread ده بيرسم الواجهة كمان. لو فيه script بياخد 200 مللي ثانية، طوال الـ 200 دي المتصفح مش قادر يرد على input ولا يرسم frame جديد. النتيجة المباشرة: ضغطة الزرار بتاخد وقت يطول قبل ما تظهر للعين، والمستخدم بيحس إن الواجهة "اتجمّدت".
قبل 2024، Google كانت بتقيس استجابة الواجهة بمقياس FID — بس FID بيقيس أول تفاعل بس. INP (Interaction to Next Paint) بيقيس كل تفاعل خلال الزيارة وبياخد أسوأ نتيجة (تقريباً P98). لو P75 عندك فوق 200ms، الموقع بياخد تقييم "Needs Improvement" في Search Console، وفوق 500ms بقى "Poor".
تخيّل المشهد ده (مثال للمبتدئ)
تخيّل كاشير واحد في فرع KFC، شغّال بيحضّر وجبة عائلية بتاخد منه 4 دقائق متواصلة. خلال الـ 4 دقايق دي، أي زبون جديد بيقف على الباب — حتى لو عاوز يدفع كوكاكولا بس وياخدها. الكاشير مش بيوقف وسط الطلب علشان يخدم الزبون السريع، فالكل بيستنى.
ده بالظبط اللي بيحصل في المتصفح: long task بيمسك الـ main thread، وكل ضغطات المستخدم بتتراص في طابور. الحل مش جيب كاشير تاني (يعني Web Worker)، الحل إن الكاشير ياخد breath كل 30 ثانية ويبص للباب لو في حد مستعجل. ده بالظبط اللي بتعمله scheduler.yield().
التعريف العلمي الدقيق
طبقاً لمواصفات WICG Scheduler API، scheduler.yield() دالة async بترجّع Promise بتتـ resolve في الـ task التالية بعد ما المتصفح يتعامل مع pending input و rendering work. هي مش setTimeout(0) — لأن setTimeout(0) بيتاخر في الـ task queue ورا أي tasks تانية اتسجلت بعديه، أما scheduler.yield بياخد "continuation" بأولوية أعلى وبيرجعك أسرع، وبيضمن إن أي pending input event يتعامل معاه قبل ما continuation شغلك يكمل.
الـ API نزل رسمياً في Chrome 129 في سبتمبر 2024، وبقى مدعوم في Edge 129 و Opera 115. Safari و Firefox لسه ما شحنوش الدعم لحد مايو 2026، فلازم fallback.
الكود قبل وبعد
السيناريو: زرار "Export" بيعمل format لـ 12,000 صف JSON قبل ما يخزّنهم في ملف CSV.
قبل: long task واحدة بتاخد 320ms
button.addEventListener('click', () => {
const rows = data.map(row => ({
id: row.id,
name: sanitize(row.name),
total: calculateTotal(row),
formatted: formatDate(row.created_at),
}));
downloadCSV(rows);
});