Debounce and Throttle
Learn delay decorators, this-preserving timers, setTimeout, setInterval, debounce, and throttle in JavaScript.
JavaScript Debounce, Throttle, and Timer Delays
This page covers timer-based control flow in JavaScript: delaying function calls, preserving or losing this after a delay,
and limiting repeated calls with debounce and throttle decorators.
Delay Decorator
This decorator wraps a function and delays its execution by a specified number of milliseconds.
It preserves this with Function.prototype.apply, so the same delayed method can be attached to different objects without losing context.
const obj1 = {
info: "obj1 info",
showInfo(...args) {
console.log(`${args}: ${this.info}`);
},
};
const obj2 = {
info: "obj2 info",
};
function delay(f, ms) {
return function (...args) {
setTimeout(() => {
// Uses apply to handle `this` dynamically
f.apply(this, args);
}, ms);
};
}
obj1.delayedShowInfo = delay(obj1.showInfo, 1000);
obj2.delayedShowInfo = delay(obj1.showInfo, 1000);
obj1.delayedShowInfo("Info", "one"); // Info,one: obj1 info
obj2.delayedShowInfo("Info"); // Info: obj2 infoDelaying function execution with and without context (this)
This version delays a standalone function with setTimeout, but it does not preserve this when used with object methods.
For object methods, Function.prototype.bind or apply should be used to retain proper context.
function delay(f, ms) {
return function (...args) {
setTimeout(() => {
f(...args);
}, ms);
};
}
function showDetails(name, age) {
console.log(`Name: ${name}, Age: ${age}`);
}
const delayedShowDetails = delay(showDetails, 1000);
delayedShowDetails("Alice", 25);
// Name: Alice, Age: 25
const obj1 = {
info: "obj1 info",
showInfo(prefix) {
console.log(`${prefix}: ${this?.info}`);
},
};
obj1.delayedShowInfo = delay(obj1.showInfo, 1000);
obj1.delayedShowInfo("Info", "one"); // Info: undefinedOutput Every Second
Both versions print numbers at one-second intervals. setInterval repeats until cleared. Recursive setTimeout schedules the next run only after the current run finishes, which gives better control when work duration can vary.
setInterval
function printNumbers(from, to) {
let current = from;
let timerId;
function go() {
console.log(current);
if (current === to) {
clearInterval(timerId);
}
current++;
}
go();
timerId = setInterval(go, 1000);
}
printNumbers(5, 10);
// 5 immediately, and 6 to 10 with 1s betweensetTimeout
function printNumbers(from, to) {
let current = from;
function go() {
console.log(current);
if (current < to) {
setTimeout(go, 1000);
}
current++;
}
go();
}
printNumbers(5, 10);
// 5 immediately, and 6 to 10 with 1s betweenThrottle and Debounce Decorators
Debounce and throttle both limit calls, but they answer different questions:
debouncewaits for quiet time, then runs once.throttleallows a call immediately, then blocks further calls until the time window ends.
debounce
function debounce(func, ms) {
let timeout;
return function (...args) {
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(this, args), ms);
};
}
const timeLoggedConsoleLog = (...args) => {
console.log(`Logged after ${Date.now() - startTime} ms:`, ...args);
};
const startTime = Date.now();
const f = debounce(timeLoggedConsoleLog, 500);
f("a");
setTimeout(() => f("b"), 200);
setTimeout(() => f("c"), 600);
setTimeout(() => f("d"), 600);
setTimeout(() => f("e"), 600); // Logged after 1118 ms: ethrottling
function throttle(fn, limit) {
let inThrottle;
return function (...args) {
if (inThrottle) return;
fn.apply(this, args);
inThrottle = true;
setTimeout(() => (inThrottle = false), limit);
};
}
const timeLoggedConsoleLog = (...args) => {
console.log(`Logged after ${Date.now() - startTime} ms:`, ...args);
};
const startTime = Date.now();
const f = throttle(timeLoggedConsoleLog, 500);
f("a"); // Logged after 0 ms: a
setTimeout(() => f("b"), 200);
setTimeout(() => f("c"), 600); // Logged after 613 ms: c
setTimeout(() => f("d"), 600);
setTimeout(() => f("e"), 600);Core Concepts and Patterns
Practice JavaScript core behavior with this, object methods, loops, Map conversion, async generators, recursion, promises, Proxy, and closures.
Convert Nested Arrays to a JavaScript Dictionary
Convert deeply nested JavaScript arrays into object dictionaries for fast lookup, traversal, and category data access.