How to throttle a function
Short answer
Throttle lets the function run at most once per interval and ignores the calls in between:
function throttle(fn, ms) {
let last = 0;
return (...args) => {
const now = Date.now();
if (now - last >= ms) {
last = now;
fn(...args);
}
};
}
const log = throttle((n) => console.log('call', n), 100);
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
for (let i = 1; i <= 10; i++) {
log(i);
await sleep(30);
}Output:
call 1
call 4
call 8On scroll
The scroll event fires dozens of times per second. Throttle keeps the handler cheap:
window.addEventListener('scroll', throttle(() => {
backToTopButton.hidden = window.scrollY < 500;
}, 200));Debounce or throttle
Debounce waits for a pause and calls once at the end: good for search fields. Throttle calls regularly while events keep coming: good for scrolling, resizing and mouse movement.
input.addEventListener('input', debounce(search, 300));
window.addEventListener('resize', throttle(updateLayout, 200));Common mistake
This simple throttle drops the last call. If the final state matters, for example the final scroll position, add a trailing call with a timer:
function throttle(fn, ms) {
let last = 0;
let timer;
return (...args) => {
const now = Date.now();
clearTimeout(timer);
if (now - last >= ms) {
last = now;
fn(...args);
} else {
timer = setTimeout(() => {
last = Date.now();
fn(...args);
}, ms - (now - last));
}
};
}
const log = throttle((n) => console.log('call', n), 100);
log(1);
log(2);
log(3);Output:
call 1
call 3Functions used in this recipe
- setTimeout — Runs a function once after a delay.
- Date.now — Returns the current time as a number of milliseconds.