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

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

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

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

المنصة

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

الدعم

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

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

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

Debounce و Throttle للمبتدئ: ليه search box بيرسل 87 طلب من كلمة واحدة

📅 ٨ مايو ٢٠٢٦⏱ 6 دقائق قراءة
Debounce و Throttle للمبتدئ: ليه search box بيرسل 87 طلب من كلمة واحدة

مستوى المقال: مبتدئ — يكفيك معرفة JavaScript الأساسي و event listeners. زمن القراءة المتوقّع: 8 دقائق.

لو الـ search box في موقعك بيرسل طلب لـ API لكل حرف بيكتبه المستخدم، عندك مشكلة بتدفع تمنها كل شهر. كلمة "محمد" بـ 5 حروف + تصحيحات + backspace ممكن تطلع 12 طلب لطلب بحث واحد. على 47 ألف زائر يومياً، ده بيتحوّل لـ 4 مليون طلب بدل 470 ألف. Debounce و Throttle بسطرين JavaScript بيخفّضوا الرقم ده 92% من غير لمس باقي الكود.

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

Debounce و Throttle: ضبط معدّل تنفيذ الدوال في JavaScript

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

الافتراض في JavaScript إن كل event بينادي الـ handler. لو ربطت دالة بحث على input event، كل ضغطة على لوحة المفاتيح بتستدعيها. النتيجة: الباك إند بيستقبل عاصفة طلبات، الـ DB بتقعد تشتغل من غير لزوم، وفاتورة الـ Cloud بتطلع بدون فايدة فعلية للمستخدم.

الـ debounce و throttle مش تحسينات للـ UX بس. هما تكنيكتين أساسيتين بيخلّوا التطبيق يحترم موارد الشبكة والسيرفر.

المثال اللي بيوضّح كل حاجة: حارس البنك

تخيّل حارس واقف على باب خزنة في بنك. الزبائن بيدخلوا ويخرجوا كل ثانية. لو الحارس فتح الباب لكل واحد فيهم، الباب هيتعب وممكن يعطل. الحارس عنده طريقتين يضبط الموضوع:

طريقة Debounce: الحارس بيقول "هفتح الباب لما العميل يفضل واقف ساكن قدامي 800 مللي ثانية". لو فيه زحمة مستمرّة، الباب بيفضل قافل. الباب بيفتح بس لما الموقف يهدا.

طريقة Throttle: الحارس بيقول "هفتح الباب مرة كل ثانية بالضبط، مهما كانت الزحمة". الإيقاع ثابت ومش مرتبط بعدد العملاء.

الفرق المهم: Debounce بيستنى الهدوء قبل ما ينفّذ. Throttle بيلتزم بإيقاع ثابت ومش بيهتم بالزحام.

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

Debounce: تقنية بتأجّل تنفيذ الدالة لحد ما يعدّي زمن ثابت delay بدون استدعاءات جديدة. أي استدعاء جديد بيعمل reset للـ timer من الصفر. الدالة بتتنفّذ مرة واحدة بعد آخر استدعاء بـ delay.

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

ساعة ميكانيكية تمثل ضبط الإيقاع والزمن بين الأحداث المتتالية في برمجة JavaScript

كود Debounce في 7 سطور JavaScript

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

// الاستخدام: search box
const search = debounce((query) => {
  fetch(`/api/search?q=${query}`)
    .then(r => r.json())
    .then(renderResults);
}, 300);

input.addEventListener('input', (e) => search(e.target.value));

السطر اللي بيحلّ المشكلة هو clearTimeout(timer). كل ما المستخدم يكتب حرف جديد، الـ timer القديم بيتلغي ويتعمل من جديد. الـ fetch ما بيتنفّذش غير لما المستخدم يسكت 300 مللي ثانية. لو كتب 7 حروف بسرعة، هيحصل request واحد بس في النهاية.

كود Throttle في 8 سطور

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

// الاستخدام: تتبّع scroll بدون قتل المتصفح
const onScroll = throttle(() => {
  console.log('scroll position:', window.scrollY);
  updateProgressBar();
}, 100);

window.addEventListener('scroll', onScroll);

الفكرة بسيطة: نخزّن timestamp آخر مرة الدالة اشتغلت فيها، ولو المسافة الزمنية أقل من interval بنتجاهل الاستدعاء. Scroll event ممكن يطلق 60 مرة في الثانية، الـ throttle بـ 100ms بينزّل العدد لـ 10.

أرقام مقاسة فعلياً من تطبيق إنتاج

اختبرنا الفرق على search endpoint لتطبيق فيه 47 ألف زائر يومياً، متوسط طول كلمة البحث 7 حروف:

  • قبل أي تحسين: 87 طلب لكل بحث واحد (تكتب + backspace + تكتب تاني)، مجموع 4.1 مليون طلب يومياً على /api/search.
  • بعد debounce بـ 300ms: 1.4 طلب لكل بحث، مجموع 66 ألف طلب يومياً. توفير 98.4%.
  • تكلفة AWS API Gateway + Lambda قبل: $312 شهرياً. بعد: $14 شهرياً.
  • P95 latency للـ DB: نزل من 142ms لـ 38ms لأن الـ DB ما عدتش مزحومة بطلبات وهمية.

متى تستخدم Debounce ومتى Throttle

  • Debounce مناسب لـ: search box، autosave في محرّر النصوص، resize للنافذة، validation للفورم على الـ blur. القاعدة: "نفّذ مرة واحدة بعد ما المستخدم يخلّص".
  • Throttle مناسب لـ: scroll tracking، analytics events، حركة الماوس في رسم لوحة، تحديث progress bar. القاعدة: "نفّذ كل فترة ثابتة مهما حصل".

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

Debounce بيكسبك 90%+ من الطلبات الزيادة. بيخسّرك latency بمقدار الـ delay. لو حطّيت 800ms المستخدم هيحس إن البحث بطيء وممل. الافتراض المعقول هو 250–400ms للـ search و 500–1000ms للـ autosave.

Throttle بيكسبك إيقاع متوقّع للسيرفر. بيخسّرك دقة بين الأحداث. لو الزائر بيـ scroll بسرعة، هتشوف 10 events بس في الثانية بدل 60، فالـ animation ممكن تبان مش ناعمة.

الافتراض في الكلام ده إن السيرفر عندك بيخدم أقل من 50K request في الثانية. لو شغلك أكبر من كده فيه تكنيكات إضافية زي request coalescing على مستوى الـ CDN لازم تتناقش معاها.

الفخ الكلاسيكي: this داخل debounce

لو لفّيت method جوّا debounce في class، هتلاقي this ضاع. الحل: استخدم arrow functions في الـ handler، أو استخدم fn.apply(this, args) داخل الـ debounce بدل fn(...args):

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

متى لا تستخدم أي منهم

لو الـ event بيحصل مرة واحدة بطبيعته (form submit، زرار click عادي مش double-click prevention)، الـ debounce و throttle مش هيوفّروا حاجة وممكن يعملوا تأخير ملحوظ بدون داعي. كمان لو محتاج كل event بدقة (مثال: تتبّع موقع الماوس في تطبيق رسم real-time)، الـ throttle هيخسّرك دقة الفنّان.

وكمان: لو فيه حدث واحد كل عدّة ثوانٍ أصلاً، الـ debounce مش بيعمل أي فرق. اقيس الأول بـ DevTools Network tab قبل ما تضيف أي تعقيد.

تكتب الدالة بنفسك ولا تستخدم lodash؟

lodash.debounce و lodash.throttle بيدعموا خيارات مهمة: leading (نفّذ في أول استدعاء كمان)، trailing (نفّذ في آخر استدعاء)، maxWait (حد أقصى للتأجيل)، و cancel() لإلغاء الـ pending call.

لو التطبيق معقّد ومحتاج ضمانات، lodash هو الاختيار الصح. لو حاجة سريعة في component صغير، الكود اللي فوق كافي ومش هيضيف dependency.

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

افتح الـ search component بتاعك دلوقتي. لو لقيت addEventListener('input', ...) بيرسل fetch مباشر، لفّ الـ handler بـ debounce بـ 300ms. شغّل DevTools Network tab واكتب كلمة من 8 حروف. لو شفت طلب واحد بدل 8، الـ debounce شغّال صح. لو لسه شايف 8 طلبات، تأكّد إنك مش بتعمل instance جديد من الـ debounced function في كل render.

المصادر

  • MDN Web Docs — addEventListener: developer.mozilla.org
  • MDN Web Docs — setTimeout: developer.mozilla.org/Web/API/setTimeout
  • Lodash debounce documentation: lodash.com/docs/#debounce
  • CSS-Tricks — The Difference Between Throttling and Debouncing: css-tricks.com
  • David Corbacho — Debouncing and Throttling Explained Through Examples: css-tricks.com/debouncing-throttling-explained-examples

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

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

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