What is Throttling?
Throttling is a technique for limiting the number of times a function can be called within a specified time. Unlike debouncing, throttled functions execute immediately when first called and ignore subsequent calls until a cooldown period has passed.
How to Use Throttle
The throttle function creates a new function that wraps your original function with throttling behavior:
let counter = 0
const increment = () => {
counter += 1;
console.log(counter);
}
const throttledIncrement = throttle(increment, 500);
// First call executes immediately
throttledIncrement(); // logs 1
// These calls are ignored during the 500ms cooldown
throttledIncrement();
throttledIncrement();
throttledIncrement();
// After 500ms passes, the function can be called again
setTimeout(() => {
throttledIncrement(); // logs 2
}, 600);
Real-World Applications
Throttling is particularly useful when dealing with events that fire rapidly. Think about scroll events on a webpage - without throttling, a scroll handler might fire hundreds of times during a single scroll action. With throttling, you can limit it to executing maybe once every 100ms, which is more efficient.
Building Our Own Throttle Function
Alright, let's build our throttle function step by step. First, we'll start with the basic function signature:
function throttle(func: Function, wait: number): Function {
}
We know throttle returns a new function, so let's add that:
function throttle(func: Function, wait: number): Function {
return function() {
}
}
Now, we need to track whether the function is in its cooldown period. For this, we'll use a variable in the closure:
function throttle(func: Function, wait: number): Function {
let timer: number | null;
return function() {
}
}
The throttling logic is straightforward:
- If the timer exists (we're in cooldown), ignore the call
- Otherwise, execute the function and start the cooldown timer
function throttle(func: Function, wait: number): Function {
let timer: number | null;
return function() {
if (timer) return;
func();
timer = setTimeout(() => { timer = null }, wait);
}
}
This basic version works, but we're missing a couple of important things. We need to handle the this
context and any arguments passed to the throttled function:
function throttle(func: Function, wait: number): Function {
let timer: number | null;
return function(this: any, ...args: any[]) {
if (timer) return;
func.apply(this, args);
timer = setTimeout(() => { timer = null }, wait);
}
}
And that's our complete throttle function! Notice we're using a regular function instead of an arrow function for the returned function. This is important because we want this
to refer to where the function is called, not where it's defined.
Also, we're using func.apply(this, args)
to ensure the original function gets both the correct context and all the arguments passed to the throttled function.
The final piece is the setTimeout callback that resets our timer to null after the wait period, which allows the function to be called again.
That's it! We've built a throttle function from scratch!
Throttle vs. Debounce: When to Use Each
The key difference is in timing:
- Throttling executes immediately and then waits
- Debouncing waits first and then executes
Use throttling when you need immediate response but want to limit subsequent calls. Use debouncing when you want to wait until activity has completely stopped.
If you've read my previous article on debouncing, you'll notice the approaches are similar but solve different problems. Both techniques use closures and timeouts, but they implement different timing strategies to control function execution.
Further Reading & Resources
- setTimeout
- Function.prototype.apply
- Closures
- this keyword
- JavaScript Debouncing from Scratch - My previous article on debouncing
A Personal Note
Similar to my debounce article, I wrote this throttle explanation to solidify my understanding of the concept. Working on these fundamental JavaScript utilities has helped me appreciate how elegant solutions can be built with just a few lines of code.
By exploring both throttling and debouncing, I've gained a better understanding of when to use each technique in real-world applications. I hope these articles help others who are learning these concepts too!
Top comments (0)