Debounce و Throttle: الفرق الحقيقي في الكود
المشكلة باختصار
Events زي input وscroll وresize وmousemove بتتنادى بمعدل رهيب. الـ scroll لوحده ممكن يتنادى 100 مرة في الثانية على شاشة 120Hz. لو handler بيحسب حاجة ثقيلة أو بيبعت fetch، الصفحة بتتجمّد والفاتورة على backend بتطلع من غير داعي.
مثال لمبتدئين: جرس الباب والضيوف
تخيّل إن عندك جرس بيرنّ في بيتك. الضيوف بيدوسوا عليه كل ثانيتين على الباب. عندك طريقتين تتعامل:
- Debounce: استنّى لحد ما الضيف يبطّل يدوس على الجرس لمدة 3 ثواني كاملة، وبعدين افتح الباب مرة واحدة. لو هو لسه بيدوس، ارمي العدّاد وابدأ من الأول.
- Throttle: افتح الباب كل 5 ثواني مهما دقّ الجرس. لو دقّ عشرين مرة في الخمس ثواني دول، اعتبرهم دقّة واحدة.
Debounce بيجمّع الضغطات وبيستنّى الهدوء. Throttle بيحدّد معدل ثابت ميتعداش.
التعريف العلمي الدقيق
Debounce
دالة بتأجّل تنفيذ function معيّنة لحد ما يعدّي وقت محدد (مثلاً 300ms) من آخر استدعاء. لو استدعيتها تاني قبل ما الوقت ده يخلّص، الـ timer بيترمى ويبتدي من جديد. النتيجة: الـ function بتتنفّذ مرة واحدة في نهاية سلسلة الأحداث.
Throttle
دالة بتضمن إن الـ function بتتنفّذ مرة واحدة بحد أقصى كل فترة محددة (مثلاً كل 200ms)، حتى لو الـ event اتنادى 100 مرة في الفترة دي. النتيجة: معدل استدعاء ثابت ومتوقّع.
كود شغّال: Search Box بـ Debounce
دي الحالة الأشهر. المستخدم بيكتب، واحنا مش عايزين نبعت طلب للسيرفر مع كل حرف — عايزين نستنّاه يخلّص الكلمة.
function debounce(fn, delay = 300) {
let timerId;
return function (...args) {
clearTimeout(timerId);
timerId = setTimeout(() => fn.apply(this, args), delay);
};
}
const searchAPI = (query) => {
console.log(`Fetching: ${query}`);
return fetch(`/api/search?q=${query}`);
};
const debouncedSearch = debounce(searchAPI, 300);
document.querySelector('#search').addEventListener('input', (e) => {
debouncedSearch(e.target.value);
});
لو المستخدم كتب كلمة "javascript" بسرعة عادية (10 حروف في 1.5 ثانية)، الكود ده هيبعت request واحد بس بدل 10.