Episode 1 — Fundamentals / 1.17 — Additional JavaScript Topics
1.17.a — Throttling & Debouncing in JavaScript
In one sentence: Throttling guarantees a function runs at most once per time window while events keep firing; debouncing waits until events stop for a chosen delay — both reduce wasted work on
scroll,resize, andinput.
Navigation: ← 1.17 Overview · 1.17.b — JSON.parse & JSON.stringify →
1. Why they exist
Handlers for scroll, resize, mousemove, and input can fire tens or hundreds of times per second. Heavy work (DOM measurement, layout, network) on every tick causes jank and drains battery. Throttle and debounce are rate-control patterns — not browser built-ins, but small wrapper functions you write or import from Lodash (_.throttle, _.debounce).
2. Throttling
Definition: After the wrapped function runs, ignore further calls until wait milliseconds have passed; then the next call may run.
Use when:
- Updating a sticky header or scroll spy on scroll
- Resizing canvas or recomputing layout periodically, not on every pixel
function throttle(fn, wait) {
let last = 0;
return (...args) => {
const now = Date.now();
if (now - last >= wait) {
last = now;
fn(...args);
}
};
}
window.addEventListener(
"scroll",
throttle(() => {
console.log("At most once per 200ms while scrolling");
}, 200),
{ passive: true }
);
Trailing edge: the pattern above fires on the first eligible event after the window; variants also fire a final call when scrolling stops (leading/trailing options in libraries).
3. Debouncing
Definition: Each call resets a timer; the function runs only after no new calls for delay ms.
Use when:
- Search-as-you-type — only
fetchafter user pauses typing - Window resize — run layout when user finished dragging the handle
- Auto-save a draft after user stops editing
function debounce(fn, delay) {
let id;
return (...args) => {
clearTimeout(id);
id = setTimeout(() => fn(...args), delay);
};
}
input.addEventListener(
"input",
debounce((e) => {
console.log("Query after pause:", e.target.value);
}, 300)
);
4. Throttle vs debounce — quick pick
| Situation | Prefer |
|---|---|
| Keep UI in sync while user scrolls | Throttle (or requestAnimationFrame for visual updates) |
| Network or expensive work after typing stops | Debounce |
| Resize: recalc once drag ends | Debounce |
| Resize: progressive preview while dragging | Throttle |
5. requestAnimationFrame (related)
For visual updates tied to paint, requestAnimationFrame runs ~once per frame — often better than a raw scroll listener doing layout. Combine with throttle when you also need non-visual side effects.
6. Key takeaways
- Throttle caps frequency; debounce delays until quiet.
- Use
{ passive: true }on scroll when you do notpreventDefault(1.15.e). - Prefer libraries or battle-tested snippets for leading/trailing edge semantics in production.
Explain-It Challenge
Explain without notes:
- For a live search that calls an API, why is debounce usually better than throttle?
- Why might throttle be better than debounce for updating a reading progress bar on scroll?
- What is the difference between
clearTimeoutinside debounce and “doing nothing” between scroll events?
Navigation: ← 1.17 Overview · 1.17.b — JSON.parse & JSON.stringify →