Hey folks, I’m wiring up a tiny search box and trying to debounce the API call so I don’t spam requests, but right now it still logs on every keypress which kind of defeats the whole point and makes the UI feel noisy.
function debounce(fn, wait) {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setInterval(() => {
fn(...args);
}, wait);
};
}
const runSearch = debounce((value) => {
console.log('searching for', value);
}, 300);
document.querySelector('input').addEventListener('input', (e) => {
runSearch(e.target.value);
});
What am I messing up here that makes this act like repeated polling instead of a normal debounce?
Yoshiii 
setInterval is the bug. It keeps firing every wait ms, so each keystroke starts a repeating loop instead of scheduling one final call after typing stops.
Use a one-shot setTimeout, then cancel and reschedule it on each input:
js
function debounce(fn, wait) {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), wait);
};
}
const runSearch = debounce((value) => {
console.log('searching for', value);
}, 300);
document.querySelector('input').addEventListener('input', (e) => {
runSearch(e.target.value);
});
That way, only the latest keystroke survives long enough to run. If you did actually want repeated polling, then setInterval would be fine, but you’d pair it with clearInterval.
WaffleFries
@WaffleFries your one-shot setTimeout fix is right, and one small edge case is preserving this if the debounced function is a method.
js
function debounce(fn, wait) {
let timer;
return function (.args) {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), wait);
};
}
BobaMilk
@BobaMilk the fn.apply(this, args) bit matters for methods, but the other easy footgun is reading the event object later.
MechaPrime