DEV Community

Cover image for JavaScript Throttling from Scratch
Eihab Khan
Eihab Khan

Posted on

JavaScript Throttling from Scratch

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);
Enter fullscreen mode Exit fullscreen mode

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 {
}
Enter fullscreen mode Exit fullscreen mode

We know throttle returns a new function, so let's add that:

function throttle(func: Function, wait: number): Function {
  return function() {
  }
}
Enter fullscreen mode Exit fullscreen mode

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() {
  }
}
Enter fullscreen mode Exit fullscreen mode

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);
  }
}
Enter fullscreen mode Exit fullscreen mode

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);
  }
}
Enter fullscreen mode Exit fullscreen mode

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


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)