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

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

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

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

المنصة

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

الدعم

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

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

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

Debounce و Throttle للمتوسط: من 10 طلبات API لطلب واحد بسطرين

📅 ١٤ مايو ٢٠٢٦⏱ 5 دقائق قراءة
Debounce و Throttle للمتوسط: من 10 طلبات API لطلب واحد بسطرين

Debounce و Throttle: تحكّم في الأحداث المتكرّرة قبل ما تغرق السيرفر

هذا المقال يتطلب مستوى: متوسط. الافتراض إنك تكتب JavaScript مرتاح وتعرف addEventListener والـ closures، لكن لسه ما طبّقتش Debounce و Throttle بإيدك. لو انت مبتدئ، الأمثلة التوضيحية في كل قسم هتوصّلك الفكرة قبل الشرح التقني.

لو صندوق البحث في موقعك بيبعت طلب API مع كل حرف، فكلمة من 10 حروف بتولّد 10 طلبات، تسعة منهم نتيجتهم بتترمي فورًا. Debounce بيحوّلهم لطلب واحد، فبتوفّر حوالي 90% من حِمل البحث على السيرفر بسطرين كود.

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

فيه أحداث في المتصفح بتتطلق بمعدّل عالي: input مع كل ضغطة زر، scroll بيوصل من 30 لـ 60 مرة في الثانية، و resize و mousemove نفس الكلام. لو ربطت بكل حدث منهم عملية تقيلة — طلب شبكة، إعادة رسم، أو حساب layout — المتصفح هيتلكّع والسيرفر هياخد ضغط مالوش لزمة.

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

رسم توضيحي لصندوق بحث يطلق 10 طلبات API بدون debounce ثم طلبًا واحدًا فقط بعد تطبيق debounce

Debounce: استنّى لما النشاط يهدأ

المثال البسيط الأول

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

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

التعريف العلمي

Debounce هو higher-order function بياخد دالة وفترة انتظار، ويرجّع نسخة مغلّفة منها. كل استدعاء للنسخة المغلّفة بيمسح أي setTimeout سابق ويجدوِل واحد جديد. الدالة الأصلية بتتنفّذ مرة واحدة فقط، بعد آخر استدعاء بفترة الانتظار كاملة.

JavaScript
function debounce(fn, delay) {
  let timerId;
  return function (...args) {
    clearTimeout(timerId);
    timerId = setTimeout(() => fn.apply(this, args), delay);
  };
}

Throttle: إيقاع ثابت مهما زاد الضغط

المثال البسيط الأول

تخيّل أتوبيس بيتحرّك من المحطة كل 10 دقايق بالظبط. مش فارق وقف 5 ركاب ولا 50، الأتوبيس بيمشي على نفس الإيقاع، واللي جه في وقت بينّي بيستنّى الرحلة الجاية. مفيش انتظار "للهدوء" زي المصعد، فيه إيقاع ثابت.

Throttle بيعمل كده مع الأحداث: بيسمح بتنفيذ واحد كل فترة، وأي حدث بيقع جوّه الفترة دي بيتجاهله.

التعريف العلمي

Throttle بيضمن إن الدالة ما تتنفّذش أكتر من مرة واحدة كل فترة محدّدة، مهما كان معدّل الاستدعاء. التطبيق الأبسط بيخزّن وقت آخر تنفيذ ويقارنه بالوقت الحالي.

JavaScript
function throttle(fn, interval) {
  let lastRun = 0;
  return function (...args) {
    const now = Date.now();
    if (now - lastRun >= interval) {
      lastRun = now;
      fn.apply(this, args);
    }
  };
}

مثال تنفيذي: صندوق بحث حقيقي

الافتراض إن عندك صفحة فيها 14,000 مستخدم نشط يوميًا، وكل واحد بيكتب في المتوسط كلمتين بحث، كل كلمة 8 حروف. من غير Debounce ده 14000 × 2 × 8 = 224,000 طلب بحث في اليوم. مع Debounce بفترة 300 مللي ثانية، الرقم بينزل لحوالي 28,000 طلب — توفير 87%.

JavaScript
const searchInput = document.querySelector('#search');

async function fetchResults(query) {
  const res = await fetch(`/api/search?q=${encodeURIComponent(query)}`);
  return res.json();
}

const runSearch = debounce((event) => {
  const query = event.target.value.trim();
  if (query.length < 2) return;
  fetchResults(query).then(renderResults);
}, 300);

searchInput.addEventListener('input', runSearch);

ولو عايز تربط حدث الـ scroll بمؤشّر تقدّم القراءة، استخدم Throttle بدل Debounce، لأنك عايز تحديثات مستمرة أثناء التمرير مش تحديث واحد بعد ما يقف:

JavaScript
const updateProgressBar = throttle(() => {
  const scrolled = window.scrollY / document.body.scrollHeight;
  progressBar.style.width = `${scrolled * 100}%`;
}, 100);

window.addEventListener('scroll', updateProgressBar);

أفضل طريقة تختار بيها بين الاتنين

القاعدة اللي بتشتغل فعليًا: Debounce لما تهمّك النتيجة النهائية بس — البحث، التحقق من النموذج، الحفظ التلقائي. Throttle لما تهمّك التحديثات المستمرة بمعدّل معقول — الـ scroll، الـ resize، الـ mousemove، وتتبّع موضع السحب.

الـ trade-offs اللي لازم تعرفها

  • Debounce بيضيف تأخير محسوس. الـ trade-off هنا إنك بتكسب تقليل الطلبات، بتخسر إن المستخدم بيستنّى 300 مللي ثانية بعد آخر حرف. لو رفعتها لـ 800، البحث هيبان بطيء.
  • Throttle بيرمي أحداث. أي حدث جوّه الفترة المحجوزة بيتجاهل. ده مقبول للـ scroll، لكنه كارثة لو كل حدث مهم.
  • الـ closure بيمسك الـ timer في الذاكرة. لو عملت debounce جوّه دالة render بتتكرّر، بتولّد نسخة جديدة كل مرة والقديمة بتفضل معلّقة. عرّف الدالة المغلّفة مرة واحدة برّا.
  • الفرق بين leading و trailing edge. التطبيق اللي فوق بينفّذ في النهاية (trailing). فيه حالات عايز فيها التنفيذ يحصل فورًا في أول حدث (leading)، وده بيحتاج كود إضافي.

متى لا تستخدم Debounce أو Throttle

لو الحدث بيتطلق نادرًا أصلًا — زي submit أو click على زر — مفيش أي داعي للتغليف، بتضيف تعقيد بدون مكسب. وكمان لو محتاج كل حدث بدقّة (مثلًا تسجّل كل ضغطة مفتاح في محرّر تعاوني)، Debounce و Throttle هيضيّعوا بيانات. الافتراض اللي بيخلّيهم مفيدين هو إن الحدث متكرّر وإن الأحداث الوسطية مالهاش قيمة.

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

افتح أكبر معالج input أو scroll في مشروعك دلوقتي، وضيف عليه debounce أو throttle حسب القاعدة فوق. افتح تبويب Network في DevTools وقِس عدد الطلبات قبل وبعد. لو ما لقيتش فرق واضح، يبقى الدالة المغلّفة بتتعاد تعريفها في كل render — راجع مكان تعريفها.

المصادر

  • MDN Web Docs — توثيق setTimeout() و EventTarget.addEventListener() لسلوك المؤقّتات وأحداث المتصفح.
  • MDN Web Docs — مقالة "Debounce and throttle" ضمن دليل أداء الويب.
  • توثيق Lodash الرسمي — دوال _.debounce و _.throttle وخيارات leading و trailing و maxWait.
  • Chrome for Developers — إرشادات قياس أداء معالجات الأحداث وتأثير الـ long tasks على الـ main thread.

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

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

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