المستوى المطلوب: محترف. هذا المقال يفترض أنك مرتاح في event loop المتصفح، Performance Observer API، Long Tasks، وعندك خبرة سابقة في profiling عبر Chrome DevTools Performance Panel.
لو P95 INP في موقعك فوق 200ms، Google بيصنّف الصفحة Poor في Core Web Vitals، وده بيأثر على ترتيب البحث وعلى تجربة المستخدم بشكل مباشر. هنا طريقة بتنزّل P95 INP من 480ms إلى 95ms على نفس الـ logic تقريبًا، باستخدام scheduler.yield و navigator.scheduling.isInputPending.
تحسين INP للمحترف: من 480ms إلى 95ms بدون إعادة كتابة الكود
المشكلة باختصار
INP (Interaction to Next Paint) بقى Core Web Vital رسمي من 12 مارس 2024، وبيحلّ محل FID. الفرق المهم: FID كان بيقيس أول تفاعل فقط، أما INP بيقيس أسوأ تأخير بين كل تفاعلات الزائر مع الصفحة طوال الجلسة. يعني تفاعل واحد بطيء بعد دقيقة من الفتح يقدر يدمّر الـ score بتاعك بالكامل.
الحدود الرسمية من Google: ≤200ms = Good، 200–500ms = Needs Improvement، أكتر من 500ms = Poor.
تمثيل واقعي قبل التعريف العلمي
تخيّل كاشير في سوبر ماركت بيخدم زبون واحد. الزبون حط 80 منتج على السير. الكاشير قرر يخلّص الـ 80 منتج كلهم قبل ما يرد على أي زبون تاني واقف في الطابور. النتيجة: كل اللي وراه بيستنوا 7 دقايق من غير حركة. لو الكاشير وقف كل 5 منتجات وسأل "في حد محتاج حاجة سريعة؟"، الطابور كله كان هيتحرك أسرع.
المتصفح بيتعامل بنفس الطريقة. الـ JavaScript main thread بيشتغل task واحد لحد ما يخلّصه. لو الـ task أخد 480ms، أي click أو tap من اليوزر في النص هيستنّى الـ 480ms كاملة قبل ما يحصل أي visual feedback. ده بالظبط هو INP.
التعريف الدقيق من توثيق web.dev: INP هو الزمن من بداية أي تفاعل (click, tap, key press) لحد أول frame يرسم فيه المتصفح تغيير على الشاشة. الـ web-vitals library بتأخد قياس لكل تفاعلات الجلسة، وبترجّع تقريبًا P98 كقيمة نهائية للصفحة.
ليه scheduler.yield تحديدًا، مش setTimeout
الطريقة القديمة لتقطيع long tasks كانت await new Promise(r => setTimeout(r, 0)). المشكلة: setTimeout بيحط continuation الـ task في آخر طابور المهام. لو في 5 tasks تانية في النص، الشغل بتاعك هيستنّاهم كلهم. النتيجة: continuation بيتأجل 50–200ms من غير أي سبب منطقي.
scheduler.yield() بيعمل العكس: بيدّي المتصفح فرصة يخدم render و user input أولًا، وبعدين يكمّل الـ continuation من نفس الأولوية فورًا، قبل أي شغل جديد. متاح في Chrome 129+ من سبتمبر 2024، وفي Firefox 132+. للـ browsers القديمة فيه polyfill رسمي من Google داخل web-vitals.
الحل التنفيذي
افترض إن عندك function بترتّب 50,000 صف في DataGrid على client-side بعد click من اليوزر. الكود الأصلي: