// throttling
let count = 0;
function printScroll() {
count += 1;
console.log("scroll called", count);
}
function throttle(fx, delay) {
let timeoutId = null;
return function () {
if (!timeoutId) {
timeoutId = setTimeout(() => {
fx();
clearTimeout(timeoutId);
timeoutId = null;
}, delay);
}
};
}
const throttleFn = throttle(printScroll, 2000);
document.addEventListener("scroll", (e) => {
throttleFn();
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div class="div-element-aqua" style="height:300px">
element 1
</div>
<div class="div-element-lime" style="height:300px">
element 2
</div>
<script src="./app.js"></script>
</body>
</html>
const useThrottle = (func, delay) => {
let timeout = null;
return (...args) => {
if (timeout) {
return;
}
func(...args);
timeout = setTimeout(() => {
timeout = null;
}, delay);
};
};
export default useThrottle;
Now, inside the App component, call this method and pass the click handler handleClick()
and a delay of 1000ms.
const handleClickThrottled = useThrottle(handleClick, 1000);
We'll use this function as the event handler for our button.
<button onClick={handleClickThrottled}>Click Me</button>
Throttling is a mechanism that allows a function execution for a limited number of times after that it will block its execution.
Simple words:- It limits the execution of your code to once in every specified time interval. It is a practice used to improve browser performance.
Throttling is a technique used to limit the rate at which a function is called. Throttling transforms a function such that it can only be called once in a specific interval of time.
Let's understand this with an example. Let's take a function fun()
:
function fun() {
console.log('This is a function')
}
We want to modify this function so that it can only be called once in 500ms. So, throttling will take fun()
as an input, and return a modified (throttled) function throttledFun()
that can only be executed 500ms after the previous function was executed.
When you call throttledFun()
multiple times within 500ms, fun()
will only be executed the first time. You will have to wait 500ms before fun()
can be executed again. This happens after every subsequent function call. Thus, fun()
can only be called once every 500ms.
Let's first understand what we want to achieve with throttling:
- Call the function immediately the first time.
- After each call, prevent the function from being called again for a certain time period.
- Once that time period has passed, the function can be called again.
To do all this, let's first create a helper function that will return a throttled function:
function throttle(func, delay) {
return () => {} // return throttled function
}
For any use cases, the throttled function will be used instead of the original fun()
.
Let's start by simply calling the function like this:
function throttle(func, delay) {
return () => {
func()
}
}
Once the function is called, we want to block it from being called again for a certain time period delay
. Once the time has passed, we want to unblock the function. We can achieve this behaviour using setTimeout
.
For now, let's keep the setTimeout
empty. You'll understand how it works in a minute.
setTimeout(() => {}, delay)
Next, we'll declare a variable timeout
that will be initialized only once in the outer function (that is the throttle()
function). The setTimeout
method returns a unique timeout id that we can use to identify a timeout. We'll assign the current timeout id to timeout
.
function throttle(func, delay) {
let timeout=null
return () => {
func()
timeout=setTimeout(() => {
// do something
}, delay)
}
}
Since timeout
contains the id of the current timeout, we add a condition at the start of the throttled function to check if a timeout exists before calling the original function func()
.
function throttle(func, delay) {
let timeout=null
return () => {
if(!timeout) {
func()
timeout=setTimeout(() => {
// do something
}, delay)
}
}
}
Initially, timeout
is null, so the function is executed. The throttled function then starts a new timeout and assigns it to the timeout
variable. In the next function call, it checks if a timeout exists before calling func()
. If a timeout already exists, it does not execute func()
.
But what happens after the time period of delay
has passed? Inside the setTimeout
we need to do something that enables func()
to be called again. Since we are using timeout
to control the function calls, we set it to null after delay
milliseconds.
timeout=setTimeout(() => {
timeout=null
}, delay)
Now, when you call the function, it is executed and the process repeats by starting a new timeout. We have successfully throttled the function.
But there's something fundamental that we are still overlooking. In the current function call, once we assign setTimeout
to the timeout
variable, for the next one we are assuming that timeout
is still valid and holds the value that we want – even if the variable is declared inside the throttle()
function.
How is the inner function still able to have access to the variable long after the throttle()
function has finished execution? It uses a concept called a closure. Let's take a quick detour to visit this concept.
Let's see how throttling is used in practical applications. We'll take a button that makes a call to the backend server when a user clicks it. An API call is made every time someone clicks the button.
But an API request can take some time, and if the user clicks the button again, or repeatedly in a short time, more and more API calls are made which could overload the server. To avoid this behaviour, we use function throttling. Let's implement this with React.
In the App component, let's create a button with an onClick
handler that makes an API call to the Node server.
function App() {
const fetchData = async () => {
const resp = await fetch("http://localhost:8000/data");
return resp.json();
};
const handleClick = () => {
fetchData().then((data) => {
console.log(data);
});
};
return (
<div className="App">
<button onClick={handleClick}>Click Me</button>
</div>
);
}
export default App;
Now, let's click this button repeatedly.
Multiple API Calls
We'll write the throttling logic inside a custom hook. Since you may need throttling in multiple places in your application, it is recommended to put the logic inside a custom hook.
Create a new folder called custom-hooks
. Inside it, create a file useThrottle.js
. Inside the file, create and export new function useThrottle()
. The method should take a function and delay as parameters and return the throttled function.
const useThrottle = (func, delay) => {
let timeout = null;
return (...args) => {
if (timeout) {
return;
}
func(...args);
timeout = setTimeout(() => {
timeout = null;
}, delay);
};
};
export default useThrottle;
Now, inside the App component, call this method and pass the click handler handleClick()
and a delay of 1000ms.
const handleClickThrottled = useThrottle(handleClick, 1000);
We'll use this function as the event handler for our button.
<button onClick={handleClickThrottled}>Click Me</button>
Ref : throttling-in-javascript