Q45. What are debouncing and throttling? Explain with examples.
ঘন ঘন ঘটা event কমিয়ে আনার দুটি কৌশল — পার্থক্য ও বাস্তব উদাহরণ।
দুটোই উদ্দেশ্য একটাই — খুব দ্রুত ঘন ঘন ঘটা event-এর সংখ্যা কমিয়ে expensive কাজ কমাতে। পার্থক্য হলো কখন কাজটি চলে।
Debounce ইউজার কাজ শেষ করার পরে কল করে — ঘন ঘন event-এর মধ্যে শুধু শেষটা গোনা হয়। Throttling নির্দিষ্ট সময় পরপর কল করে — মাঝের event-গুলো বাদ দেওয়া হয়।
// ❌ সরাসরি — প্রতিটি অক্ষরে কাজ চলবে
input.addEventListener("input", (event) => {
console.log("খোঁজা হচ্ছে:", event.target.value);
searchOnServer(event.target.value);
});user যদি দ্রুত টাইপ করে, তাহলে প্রতিটি অক্ষরের জন্য আলাদা request যাবে — একটি শব্দে ৮টি request। এটি ঠিক করতেই debounce ও throttle ব্যবহার হয়।
// Debounce — ইউজার থামার ৩০০ms পরে একবারই কাজ করবে
function debounce(fn, delay) {
let timerId;
return function (...args) {
// আগের অপেক্ষা বাতিল
clearTimeout(timerId);
// নতুন করে অপেক্ষা শুরু
timerId = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
}
const input = document.getElementById("search");
const debouncedSearch = debounce((value) => {
console.log("খোঁজা হচ্ছে:", value);
searchOnServer(value);
}, 300);
input.addEventListener("input", (event) => {
debouncedSearch(event.target.value);
});ধাপে ধাপে — user একটি অক্ষর লিখলে setTimeout ৩০০ms-এর timer শুরু করে। কিন্তু আরেকটি অক্ষর লিখলে আগের clearTimeout দিয়ে সেই timer বাতিল হয় এবং নতুন timer শুরু হয়। তাই বারবার টাইপ করলেও timer কেউ শেষ হবে না। থামার ৩০০ms পরে শুধু শেষ অক্ষরের value দিয়ে একবারই কাজ হয়।
// Throttle — প্রতি ১ সেকেন্ডে সর্বোচ্চ একবার কাজ করবে
function throttle(fn, delay) {
let isThrottled = false;
return function (...args) {
// এখন ব্যবস্তা চলছে — ধরে ফেলা হবে
if (isThrottled) return;
fn.apply(this, args);
isThrottled = true;
setTimeout(() => {
isThrottled = false;
}, delay);
};
}
const throttledScroll = throttle(() => {
// scroll position পড়া
const current = window.scrollY;
console.log("প্রতি ১ সেকেন্ডে scrollY:", current);
}, 1000);
window.addEventListener("scroll", throttledScroll);ধাপে ধাপে — প্রথম scroll event-এ isThrottled false থাকায় কাজ চলে এবং এটি true হয়। পরের ১ সেকেন্ডের মধ্যে আসা সব event return দিয়ে বাদ পড়ে। ১ সেকেন্ড পরে setTimeout callback চলে isThrottled আবার false করে, তখন পরের event-এ কাজ চলবে।
// ✅ ES6+ তে lodash ছাড়াই লেখা যায়
function debounce(fn, delay) {
let timerId;
return function debounced(...args) {
clearTimeout(timerId);
timerId = setTimeout(() => fn.apply(this, args), delay);
};
}
// scroll-এর জন্য আলাদা ধরনের debounce
function debounceFrame(fn) {
let frameId = null;
return function frameThrottled(...args) {
if (frameId !== null) return;
frameId = requestAnimationFrame(() => {
fn.apply(this, args);
frameId = null;
});
};
}// বাস্তব উদাহরণ — resize-এ কম কাজ
const handleResize = debounce(() => {
const width = window.innerWidth;
// পরিবর্তন হলে নতুন layout গুনে দেওয়া
recalculateLayout(width);
}, 250);
window.addEventListener("resize", handleResize);// বাস্তব উদাহরণ — সাবমিট বাটন একবার
function debounceOnce(fn, delay) {
let called = false;
return function (...args) {
if (called) return;
called = true;
setTimeout(() => {
fn.apply(this, args);
called = false;
}, delay);
};
}
const submitOnce = debounceOnce((data) => {
console.log("একবারই সাবমিট:", data);
}, 1000);// ❌ মিশিয়ে ফেলে দেওয়া ভুল
const bad = debounce(throttle(fn, 1000), 500);
// debounce-এর ভেতরে ঢুকলে একটি ঘটনাই না হয়, ফলে ইউজার আটকে যেতে পারে| বিষয় | Debounce | Throttle |
|---|---|---|
| কখন কাজ করে | থামার delay পরে | প্রতি delay-তে একবার |
| ইনপুট শেষ হওয়া | প্রভাবিত করে | প্রভাবিত করে না |
| সেরা ব্যবহার | search input, form validate | scroll, mousemove, resize |
| ফলাফল | সবচেয়ে কম কল | নিয়মিত কল |
search box-এর মতো ঘন ঘন ইনপুটে debounce ভালো, কারণ শেষ লেখা পুরোটাই search হওয়া দরকার। আর scroll বা mousemove-এর মতো event-এ throttle ভালো, কারণ প্রতি কয়েক মিলিসেকেন্ডে অবস্থান জানা দরকার — debounce দিলে হঠাৎ থেমে যেতে পারে।