JavaScript: Debouncing & Throttling
Tags: javascript, codenewbie, frontend, webdev
Published: 2021-06-05 14:22:31

Hey :wave: 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 :fire: 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.

:exclamation: 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. :wink:

---

## 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2luraaomcc8xqgxudytu.png)

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9tyrp0vsvdp7lxc68tnz.png)

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](https://dev.to/thesanjeevsharma/call-apply-and-bind-in-javascript-2nno).

:star: 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. :wink:

---

## 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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/136twxxmskii7lkucitv.png)

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). :stuck_out_tongue:

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.

:point_right: 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.

---

:pray: 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](https://twitter.com/thesnjvsharma) or [LinkedIn](https://linkedin.com/in/thesanjeevsharma), for any doubts.

:earth_asia: [thesanjeevsharma.now.sh](https://thesanjeevsharma.now.sh)