🎮 Events - Performance Patterns: Debouncing & Throttling
The Story of the Hyperactive Doorbell
Imagine you have a friend who loves pressing your doorbell. Not once. Not twice. But a hundred times in a row! 🔔🔔🔔
Every time they press it, you run to the door. You’re exhausted! Your legs hurt! And by the time you get there, they’re still pressing!
This is exactly what happens in JavaScript when users type, scroll, or resize windows. Your code runs way too many times.
But what if you could be smarter about this? What if you had two magical strategies?
🧙♂️ Meet Our Two Heroes
| Hero | Superpower |
|---|---|
| Debounce | Waits until the chaos stops |
| Throttle | Limits how often things happen |
Think of them as traffic controllers for your JavaScript events!
🛑 Debouncing: “Wait Until They’re Done”
The Story
Remember that hyperactive doorbell friend?
Debouncing says: “I’ll wait. When they STOP pressing for 2 seconds, THEN I’ll answer.”
No more running to the door 100 times. You wait. They stop. You go once. Smart!
Real Life Examples
- 🔍 Search box: Wait until user stops typing, THEN search
- 📝 Auto-save: Wait until user stops writing, THEN save
- 📱 Resize: Wait until window stops resizing, THEN recalculate
How It Works
graph TD A["User types 'H'"] --> B["Timer starts: 300ms"] B --> C["User types 'e'"] C --> D["Timer RESETS: 300ms"] D --> E["User types 'l'"] E --> F["Timer RESETS: 300ms"] F --> G["User stops typing"] G --> H["Timer completes!"] H --> I["🎉 Function runs ONCE"]
The Code
function debounce(func, delay) {
let timer;
return function(...args) {
// Cancel previous timer
clearTimeout(timer);
// Start new timer
timer = setTimeout(() => {
func.apply(this, args);
}, delay);
};
}
Using Debounce
// Without debounce: runs 50 times!
input.addEventListener('input', search);
// With debounce: runs ONCE
const smartSearch = debounce(search, 300);
input.addEventListener('input', smartSearch);
🎯 Key Insight
Debouncing is like a patient waiter.
They don’t rush to take your order every time you say “umm…”
They wait until you’re DONE deciding. Then they write it down once.
⏱️ Throttling: “Only Once Every X Seconds”
The Story
Imagine a water faucet that can ONLY drip once per second. No matter how hard you turn it, one drip per second. That’s throttling!
Or think of a speed limit. Your car CAN go 200mph. But the limit is 60. You’re throttled.
Real Life Examples
- 📜 Scroll events: Update position at most 10 times per second
- 🎮 Game loop: Fire bullets at most once per second
- 📊 Analytics: Send data at most once every 5 seconds
How It Works
graph TD A["Event fires"] --> B{Allowed to run?} B -->|Yes| C["Function runs"] C --> D["Lock for 100ms"] D --> E["Unlock"] B -->|No, locked| F["Ignored"] E --> G["Ready for next"]
The Code
function throttle(func, limit) {
let inThrottle = false;
return function(...args) {
if (!inThrottle) {
func.apply(this, args);
inThrottle = true;
setTimeout(() => {
inThrottle = false;
}, limit);
}
};
}
Using Throttle
// Without throttle: runs 100+ times!
window.addEventListener('scroll', updateNav);
// With throttle: runs every 100ms max
const smartNav = throttle(updateNav, 100);
window.addEventListener('scroll', smartNav);
🎯 Key Insight
Throttling is like a bouncer at a club.
“One person in every 10 seconds. I don’t care how long the line is!”
🤔 Debounce vs Throttle: When to Use What?
The Simple Rule
| Situation | Use This | Why |
|---|---|---|
| User is typing | Debounce | Wait until they finish |
| User is scrolling | Throttle | Regular updates while scrolling |
| Window resizing | Debounce | Wait until resize ends |
| Button spam | Throttle | Allow one click per second |
| Search input | Debounce | Search when done typing |
| Game controls | Throttle | Limit action frequency |
Visual Comparison
User Actions: ||||||||||||||||||||| (many rapid events)
Debounce: ___________________X (waits, fires ONCE at end)
Throttle: X_____X_____X_____X_ (fires at regular intervals)
🌟 Real World Example: Search Box
Without Any Pattern (BAD)
searchInput.addEventListener('input', (e) => {
// Runs on EVERY keystroke!
fetch(`/search?q=${e.target.value}`)
.then(res => res.json())
.then(showResults);
});
// User types "pizza"
// 5 API calls: "p", "pi", "piz", "pizz", "pizza"
// Wastes bandwidth and server resources!
With Debounce (GOOD)
const debouncedSearch = debounce((query) => {
fetch(`/search?q=${query}`)
.then(res => res.json())
.then(showResults);
}, 300);
searchInput.addEventListener('input', (e) => {
debouncedSearch(e.target.value);
});
// User types "pizza"
// 1 API call: "pizza" (after 300ms pause)
// Efficient and smart!
🎨 Real World Example: Scroll Progress
Without Throttle (BAD)
window.addEventListener('scroll', () => {
// Runs 100+ times per second!
updateProgressBar();
checkInfiniteScroll();
updateNavHighlight();
});
// Browser becomes laggy and slow
With Throttle (GOOD)
const throttledScroll = throttle(() => {
updateProgressBar();
checkInfiniteScroll();
updateNavHighlight();
}, 100);
window.addEventListener('scroll', throttledScroll);
// Smooth performance: only 10 updates per second
🏆 Quick Summary
Debounce
- Waits until activity stops
- Fires once after the quiet period
- Best for: search, auto-save, resize
Throttle
- Allows activity at regular intervals
- Fires multiple times but limited
- Best for: scroll, mouse move, game loops
💡 Pro Tips
- 300ms is a good debounce delay for typing
- 100ms is a good throttle limit for scroll
- Always clear timers when component unmounts
- These patterns save battery on mobile devices
- Use them to reduce API calls and server load
🎉 You Did It!
You now understand two of the most important performance patterns in JavaScript!
Debounce = “Calm down, wait until they’re done”
Throttle = “Slow down, only this often”
Use them wisely, and your apps will be fast, smooth, and efficient! 🚀
