Expert · Functional patterns and testing · topic 49 of 61 · 30 min
Memoization, debounce and throttle
Explanation
Memoization: remember answers
If a pure function is slow and is called again and again with the same arguments, you can store each result in a cache (a Map) the first time and return the stored answer afterwards. This trade of memory for speed is called memoization.
function memoize(fn) {
const cache = new Map();
return (arg) => {
if (cache.has(arg)) return cache.get(arg);
const result = fn(arg);
cache.set(arg, result);
return result;
};
}
const slowSquare = (n) => { for (let i = 0; i < 1e7; i++); return n * n; };
const fastSquare = memoize(slowSquare);
fastSquare(9); // slow the first time
fastSquare(9); // instant: read from the cacheClassic use: recursive Fibonacci becomes fast when each fib(n) is cached, going from exponential time to linear.
Debounce: wait for a pause
A search box that calls the server on every key press sends 10 requests for "chicken bi". Debouncing delays the call until the user has stopped typing for, say, 300 ms, then calls once with the latest value. Each new call cancels the waiting one and restarts the timer.
function debounce(fn, waitMs) {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), waitMs);
};
}
const search = debounce((q) => console.log('Searching for', q), 300);
search('c'); search('ch'); search('chi'); search('chicken');
// after 300 ms of silence: Searching for chicken (only once)Throttle: at most once per window
Throttling lets the function run immediately, then ignores calls until a time window has passed. Use it for events that fire continuously, like scroll (dozens of times per second), where you want regular updates but not every single one.
function throttle(fn, windowMs) {
let last = 0;
return (...args) => {
const now = Date.now();
if (now - last >= windowMs) {
last = now;
fn(...args);
}
};
}
const onScroll = throttle(() => console.log('update progress bar'), 200);
// scrolling for 1 second calls onScroll ~60 times but logs about 5 times| Technique | Runs | Typical use |
|---|---|---|
| Memoize | Once per unique input, then from cache | Expensive pure calculations |
| Debounce | Once, after calls stop for N ms | Search box, auto-save, resize end |
| Throttle | At most once every N ms | Scroll, mouse move, game input |
Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
Memoization, debounce and throttle · 1/6
In this lesson you will make code faster and calmer with memoization, debounce and throttle.
Prefer a video by a teacher? Find free videos on “javascript debounce vs throttle memoization interview” on YouTube ↗
In real life: A pincode lookup on a checkout page
A checkout page shows the city and state as the user types a 6-digit pincode. Without debounce, typing "560001" would call the pincode API six times. With a 300 ms debounce it calls once. Results are memoized, so if the user edits and returns to the same pincode, no new request is needed.
const lookups = [];
const cityFor = (pin) => { lookups.push(pin); return pin.startsWith('56') ? 'Bengaluru' : 'Unknown'; };
const memo = new Map();
const cachedCity = (pin) => memo.get(pin) ?? (memo.set(pin, cityFor(pin)), memo.get(pin));
cachedCity('560001'); cachedCity('560001'); cachedCity('560001');
console.log(lookups.length); // 1Practice (0/3 done)
Write memoize(fn) for one-argument functions. The test counts how many times the real function runs.
Write debounce(fn, waitMs). Calls made within waitMs of each other should result in ONE call of fn, with the latest arguments, waitMs after the last call.
Write throttle(fn, windowMs, now = Date.now). fn runs on the first call, then calls are ignored until windowMs has passed since the last run. The now parameter lets the test use a fake clock.
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- Testing your codeFunctional patterns and testing · same module
- Timers: running code laterFrontend Web Development · JavaScript in the browser: DOM and events
- Memoization: remembering answersData Structures and Algorithms (DSA) for interviews · Recursion and backtracking
- Keeping the screen in syncFull-Stack Development · Connecting frontend and backend
- Classic coding questionsInterview preparation and career
- Embedded patterns: interrupts, timing and constraintsC Programming (from zero to systems) · Embedded C, system calls and careers
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
Which suits a search box that calls the server?
Answer: Debounce
Debounce waits until typing pauses, then calls once.
Which suits updating a progress bar while scrolling?
Answer: Throttle
Throttle gives regular, limited updates during continuous events.
When is memoization unsafe?
Answer: For functions whose result depends on changing outside data
Cached results would be stale.
In debounce, what does clearTimeout(timer) do on each call?
Answer: Cancels the previously scheduled call
Each new call cancels the waiting one and restarts the wait.
A search box calls the server on every letter typed. What fixes this?Related
Answer: Debouncing the search so it runs after the user pauses
Debounce waits for a pause in typing and then runs once with the latest text.
What is the time of fib(n) with memoization?Related
Answer: O(n)
Each value from 0 to n is computed once, with O(1) work each.
What does debouncing the search box do?Related
Answer: Waits for a short pause in typing (about 300 ms) and then sends one request
Debounce restarts a short timer on each key press, so only the final text is searched.
Why must promiseAll store results by index instead of push?Related
Answer: Promises finish in any order, but results must match input order
Push would record finishing order.
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- 2 more topics in “Functional patterns and testing” before its exam: Testing your code, Test doubles and testable design.
- Module 10 exam: Functional patterns and testing · 12 questions · 25 min, without help. 5 topics of this module still to finish first.
- Then the next module, Performance and Node.js. 61 topics left in the course before the final exam.