DEV Community

Cover image for JavaScript: Debouncing & Throttling
Sanjeev Sharma
Sanjeev Sharma

Posted on

JavaScript: Debouncing & Throttling

Hey ๐Ÿ‘‹ fellow front-end developers,

Today I'll be sharing two important concepts for optimizing function calls(or API requests) in your app. These concepts are hot ๐Ÿ”ฅ interview questions too, so you should understand them really well.

Both of these concepts are used in different situations, albeit there's minor difference in the approach.

โ— It's important that you understand closures and setTimeout before going further.

There's a really good example in the end that will make sure you never forget these concepts. ๐Ÿ˜‰


Debouncing

Let's say we have button on the screen and it has an event handler attached to it. When the button is clicked, it calls an API and gets some data.

Event Handler

But there's a problem with this code. If a user repeatedly clicks on the button in a short span of time, we execute the handler function each time. Hence, we call API multiple times and that is not optimal. What's a better solution?

By using debouncing, we can prevent unnecessary calls. Debounced function will run a timer and when this timer expires it will execute the function. But if the timer is still running and user clicks on the button again, the timer resets.
Let's say our timer waits for 1s. Even if the user clicks the multiple times, the function will only be executed 1s after the last click.

Debounce Function

I have made a generic function that can debounce any function(fn) you provide it. It also take an optional param delay, if not provided it's value is 300ms.

How does this work?

First, we wrap our function with debounce and pass time as 1000(1s). It simply means we want event handler to be executed 1s after event occurred.
debounce function returns a function as it is needed by the addEventListener function. The returned function forms a closure and has access to the timer variable always.

When user clicks on the button, we check if there's an existing timer running and clear that timer using clearTimeout. After this we initiate a new timer of 1s and store it back in timer.
If the user clicks again within next 1s, the timer resets again. Otherwise, after 1s fn is executed.

Apart from the normal flow, I have also saved the context(this) for fn and also passed arguments, if it received any. This is not required to understand debouncing.
If you want to understand function methods like call(), apply() and bind(). You can read this short article here.

โญ The most popular application of debouncing is search fields. For example, you have an e-commerce site where the user can search for products and you want to provide them suggestions as they type. Without debouncing, you'll be making API calls for every character they type as each keystroke is an event.
With debouncing, you can limit this to maybe 2 or 4 API calls(depending on the user's typing speed). Also, in the example, I have the delay as 1s but in real projects it's way less.

You can try building your own search box with debouncing now. ๐Ÿ˜‰


Throttling

If you've understood Debouncing, this will be fairly simple. This approach allows function execution after certain intervals.

For example, we've our same old button but this time it's implemented with a throttle function with a delay of 1s.

A user clicks on it repeatedly for some time.

  1. On the first click, provided fn is called.
  2. For the next 1s all the clicks will be ignored.
  3. Any click after 1s will be accepted and fn will be called again.
  4. Repeat steps 2 and 3.

When would you use this?

A good example is browser re-sizing or tracking user mouse events.
If you add debouncing to this, you would only get one function call once the user stops moving their mouse. But with throttling, you can get evenly spaced function calls even if the user keeps moving their mouse relentlessly.

Here's the implementation:
Throttle function

Just like we did for debounce, we wrap our function with throttle and pass in delay. This returns a function which can be used as event handler.

The trick is to store the time when the function was last executed. Next time when the function is called, we check if delay time has passed or not. If yes, we execute the fn and updated lastExecutedAt for the next call.

There's an alternate implementation too using setInterval but this would also suffice.


Real world analogy b/w the two

Let's say you're standing in front of an elevator. The elevator has a button next to it to open the doors. You're are repeatedly pressing the button. You're really drunk and had a fun night(before pandemic, of course). ๐Ÿ˜›

Assuming delay for the button as 3s in both the cases.

Case: Debouncing
You have been pressing the button for last 5 minutes, the elevator doesn't open. You give up and 3s later the elevator opens.

Case: Throttling
You have been pressing the button for last 5 minutes, the elevator opens at 3s intervals. First at 0s, then 3s, then 6s and so on.

๐Ÿ‘‰ It's important to note that if you press the button at 4s and 5s and then stop, no call will be made. You have to press the button after 6s to make the call. In JavaScript terms, you have to generate an event after 6s for the function to execute. There's starvation in case of throttling.


๐Ÿ™ That's it for this one. I hope you got some idea about these concepts and will use them in your projects.

You can ping me on Twitter or LinkedIn, for any doubts.

๐ŸŒ thesanjeevsharma.now.sh

Top comments (2)

Collapse
 
nguyenak95 profile image
Nguyen An Khang

Why do we need to pass context to fn.call(context, ...args) ? I don't see any value that need to bind to the execution function

Collapse
 
thesanjeevsharma profile image
Sanjeev Sharma

The throttled function can be used as a method object or as an event handler. To preserve the context, it's important to bind the actual this.