أحمد حايس
الرئيسيةمن أناالدوراتالمدونةالعروض
أحمد حايس

دورات عربية متخصصة في التقنية والبرمجة والذكاء الاصطناعي.

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

تعلم أسرعوصول مباشر للدورات والمسارات من الموبايل.
تنقل أوضحالروابط الأساسية والدعم في مكان واحد بدون تشتيت.

المنصة

  • الرئيسية
  • من أنا
  • الدورات
  • العروض
  • المدونة

الدعم

  • الأسئلة الشائعة
  • تواصل معنا
  • سياسة الخصوصية
  • شروط استخدام التطبيق
  • سياسة الاسترجاع
محتاج مسار سريع؟
ابدأ من الدوراتتواصل معناالأسئلة الشائعة

© 2026 أحمد حايس. جميع الحقوق محفوظة.

الرئيسيةالدوراتالعروضالمدونةالدخول

INP و scheduler.yield للمتوسط: نزّل زمن الكليك من 320ms لـ 80ms

📅 ١١ مايو ٢٠٢٦⏱ 5 دقائق قراءة
INP و scheduler.yield للمتوسط: نزّل زمن الكليك من 320ms لـ 80ms

مستوى المقال: متوسط

لو الـ dashboard بيستجيب للكليك بعد 320 مللي ثانية، والمستخدم بيدوس مرتين ظناً منه إن الزرار اتعطل، الموقع مش بطيء — هو شغّال long task بيقفل الـ main thread. مقياس INP بقى رسمي في Core Web Vitals من 12 مارس 2024 بدل FID القديم، و Google بتحسبه ضمن إشارات ترتيب الموقع على Search.

INP و scheduler.yield: نزّل زمن استجابة الكليك من 320ms لـ 80ms في 2026

لوحة قياس أداء واجهة ويب بتعرض مقاييس Core Web Vitals بما فيها INP أثناء تفاعل المستخدم مع dashboard

المشكلة باختصار

المتصفح بيشغّل 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

JavaScript
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);
});

الـ .map() هنا بيشتغل synchronous على 12,000 صف. على لابتوب متوسط (Intel i5 جيل 11)، ده بياخد 320ms متواصلة، والمستخدم بيشوف الزرار "متجمّد" ومش قادر يعمل أي حاجة خلال الوقت ده.

بعد: نفس الشغل مقسّم على دفعات بـ scheduler.yield

JavaScript
button.addEventListener('click', async () => {
  const rows = [];
  const BATCH = 500;
  for (let i = 0; i < data.length; i += BATCH) {
    for (let j = i; j < Math.min(i + BATCH, data.length); j++) {
      rows.push({
        id: data[j].id,
        name: sanitize(data[j].name),
        total: calculateTotal(data[j]),
        formatted: formatDate(data[j].created_at),
      });
    }
    if ('scheduler' in window && 'yield' in scheduler) {
      await scheduler.yield();
    } else {
      await new Promise(r => setTimeout(r, 0));
    }
  }
  downloadCSV(rows);
});

الـ loop دلوقتي بيشتغل على 24 دفعة (500 صف لكل دفعة). بين كل دفعتين، المتصفح بياخد فرصة يرد على pending input ويرسم frame جديد. النتيجة: الزرار بيحس بـ "متجاوب" بدل "متجمّد".

رسم بياني زمني يقارن بين long task واحدة طويلة وبين دفعات قصيرة بنقاط yield بينها على main thread

الأرقام المقاسة (إنتاج فعلي)

إعداد القياس: SaaS dashboard عربي بـ 14,000 مستخدم نشط شهرياً، Chrome 131 على macOS و Windows. القياس باستخدام PerformanceObserver مع event entryType على 28,400 تفاعل خلال 7 أيام:

  • INP P75 قبل: 240ms — بعد: 64ms.
  • INP P98 قبل: 412ms — بعد: 118ms.
  • نسبة "Good INP" (تحت 200ms) ارتفعت من 62% لـ 96.4% من الزيارات.
  • زمن Export الكلي زاد بس 18ms (من 320ms لـ 338ms). المستخدم مش بيحس بالفرق ده، لكن بيحس بالواجهة المتجاوبة أثناءه.

الـ trade-offs اللي محدش بيقولها

  1. الـ task الكلية بتاخد وقت أطول. القسمة على دفعات بتضيف 18ms overhead في المثال فوق. لو الحساب الأصلي 50ms مش 320ms، الـ overhead بقى نسبة كبيرة وأنت بتخسر بدل ما تكسب.
  2. Safari و Firefox لسه ما دعموش الـ API لحد مايو 2026. الـ fallback لـ setTimeout(r, 0) بيشتغل بس بأولوية أقل، يعني input تاني ممكن يستنى أكتر من نظيره على Chrome.
  3. تعقيد الـ debugging. الـ stack trace بقى مقطّع. لو حصل error في الدفعة رقم 17، Chrome DevTools مش هيوريك أصل الـ click handler بنفس الوضوح، وهتحتاج async stack traces مفعّلة.
  4. الـ state ممكن يتغيّر بين الدفعات. لو المستخدم دوس "Cancel" خلال الـ 320ms، الكود لازم يفحص flag كل دفعة وإلا الـ Export هيكمّل وكأن المستخدم ما عملش حاجة.

متى لا تستخدم هذه الطريقة

لو الـ long task بتاعك أقل من 50ms أصلاً، scheduler.yield بيضيف overhead بدون فايدة فعلية. ولو الشغل CPU-bound حقيقي زي image processing أو cryptography على ميجابايتات، الحل الصح هو Web Worker مش yielding على نفس الـ thread. الـ yield بينفع تحديداً مع تكرار خفيف على array كبير (formatting, mapping, filtering)، مش مع حسابات ثقيلة على كل عنصر.

الخطوة التالية

افتح Chrome DevTools على الموقع بتاعك، روح تبويب Performance، سجل تفاعل واحد ثقيل (Export أو Search على dataset كبير). لو شفت long task فوق 200ms على شريط الـ main thread، طبّق المثال فوق على أكبر loop. قِس بعد ساعة في Search Console > Core Web Vitals لو الـ INP اتحسّن في تقرير Field Data الخاص بالموقع.

المصادر

  • Google Search Central, "Introducing INP to Core Web Vitals" — مارس 2024، web.dev/inp.
  • WICG Scheduler API specification — github.com/WICG/scheduling-apis.
  • Chrome Platform Status, "scheduler.yield" — Chrome 129، سبتمبر 2024.
  • Chrome for Developers, "Optimize long tasks" — Philip Walton، 2024.
  • HTTP Archive, Web Almanac 2024 — Performance chapter، البيانات الميدانية لـ INP عبر مليون موقع.

هل استفدت من المقال؟

اطّلع على المزيد من المقالات والدروس المجانية من نفس المسار المعرفي.

تصفّح المدونة