Did every keypress in the search bar need an API call? how scroll or resize event can be optimized? Such questions are common in platforms like StackOverflow, GitHub, etc.
Google these questions only when we are at the middest of an optimization phase of our application or we are really be sucked at a never-ending API call loop. am I right?
All the google searches are pointing to two major terms in Javascript programming - Throttling and Debouncing
Did we need to learn these? yes! Almost all web applications need these techniques to optimize performance.
Simply Debouncing and Throttling techniques are used to limit the number of times a function can execute.
Consider a search bar in a shopping cart application, when we type each letter in the field, each time an API call is requested to the server. When we look into the network section in the browser,
there seems to be a number of pending API calls in the list.
Think about it? is this ok for your application?? Really Nope!!!
then the question arises - how to reduce the API calls in a search bar...Yes!! At last, you google it and all the search results repeatedly say- use Throttling and Debouncing buddy...!!!
Event Throttling
Throttling is a technique in which no matter how many times users fires the event, the attached function will be executed only at a regular interval.
Actually Throttling is a simple technique , that we can create using the setTimeout
function in Javascript.
setTimeout function
setTimeout
is a webAPI
provided by the browser and used as a scheduling function in Javascript. Using this function we can delay an event or function call to a period of time.
The syntax is:
let timerId = setTimeout(callbackFunction, timeToDelay);
Here the callbackFunction defines the code that needs to be executed after the timeToDelay period of time.
The setTimeout
function will return a timerId, which is a positive integer value to uniquely identify the timer created by the call to setTimeout
.This value can be passed to clearTimeout
to can the timeout.
Remember, the timerId is the key point in throttling.
//Example of setTimeout
const showMessage = function() {
console.log('show this message after 2 seconds');
};
let timerId = setTimeout(showMessage, 2000); //the showMessage function will call after 2000 ms and show the message.
Implementation
Throttling will fires the function call attached with it, once in an interval.For the below given example the scroll event will be counted with and without throttling implementation.
Without throttling
The sample index.html
file is:
<head>
<style>
div {
border: 1px solid black;
width: 300px;
height: 200px;
overflow: scroll;
}
</style>
</head>
<body>
<div id="div-body">
<p style="background-color: red; height: 700px">This is line 1</p>
<p style="background-color: blue; height: 700px">This is line 2</p>
<p style="background-color: green; height: 700px">This is line 3</p>
<p style="background-color: yellow; height: 700px">This is line 4</p>
</div>
<p>No of times event fired</p>
<p id='show-api-call-count'></p>
<script src= "script.js" />
</body>
the javascript script.js
file is:
let timerId, eventCallCount;
const divBodyDom = document.getElementById('div-body');
divBodyDom.addEventListener('scroll', function() {
const eventCallCountDom = document.getElementById('show-api-call-count');
eventCallCount= eventCallCount|| 0;
eventCallCount+= 1;
eventCallCountDom.innerHTML = eventCallCount;
});
Using Throttling
The sample index.html
file is:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Throttling in Javascript</title>
<style>
div {
border: 1px solid black;
width: 300px;
height: 200px;
overflow: scroll;
}
</style>
</head>
<body>
<div id="div-body">
<p style="background-color: red; height: 700px">This is line 1</p>
<p style="background-color: blue; height: 700px">This is line 2</p>
<p style="background-color: green; height: 700px">This is line 3</p>
<p style="background-color: yellow; height: 700px">This is line 4</p>
</div>
<p>No of times event fired</p>
<p id='show-api-call-count'></p>
<p>No of times throttling executed the method</p>
<p id="debounc-count"></p>
<script src= "script.js" />
</body>
</html>
the javascript script.js
file is:
let timerId, apiCallCount, throttlingDomCount;
const divBodyDom = document.getElementById('div-body');
function makeAPICall() {
const throttlingDom = document.getElementById('debounc-count');
throttlingDomCount = throttlingDomCount || 0;
throttlingDomCount += 1;
throttlingDom.innerHTML = throttlingDomCount;
}
function throttleFunction(func, delay) {
if(timerId) {
return;
}
timerId = setTimeout(function() {
func();
timerId = undefined;
}, delay);
}
divBodyDom.addEventListener('scroll', function() {
const apiCallCountDom = document.getElementById('show-api-call-count');
apiCallCount = apiCallCount || 0;
apiCallCount = parseInt(apiCallCount) + 1;
apiCallCountDom.innerHTML = apiCallCount;
throttleFunction(makeAPICall, 200);
});
Result:
Explanation
Here the throttle() function will handle the makeAPICall() and pass an interval value as 200.
so the throttle() will make an arrangement to trigger the makeAPICall() function in an interval of 200ms.
Inside the throttle() function the main point is the timerId.
If the timerId is undefined
, then the setTimeout
function will trigger and timerId returns.
If the timerId is a valid one, that means one setTimeout
function is pending to complete.right? so it will return without doing anything. That means the makeAPICall() function will be executed only when the timerId is set. That occurs only after completion of each setTimeout
function.
By setting the passing delay as the delay parameter of the setTimeout
function, we can execute the makeAPICall() function in a regular interval of 200ms.
Also, don't forget to reset the timerId to undefined
, then only the next event trigger will do as our wish.
Really simple..Yeah??
Conclusion
The developer can control the execution of events with a time interval for like, window resize, repeated button click, speedy search type, mouse move event, etc using Event throttling concept.
How can we do some action only after the completion of an event? - Using Debouncing(). Wait for my next blog!!!
Top comments (10)
Don't pay attention to people telling you optimization is not important, it is. Debouncing and throttling should be mandatory from the start when working with certain events like scrolling and HTTP calls.
A very nice tool is requestAnimationFrame
developer.mozilla.org/it/docs/Web/...
Essentially, it's a throttling function triggering a callback only upon browser re-paints, so that you can kind of delegate the optimization straight to the browser and forget about setTimeout, setInterval and such, at least for throttling.
Cool nice post.
I'd like to add a little disclaimer for young and junior devs, explaining that premature optimization can be evil in some cases... I think it's good to explain to junior devs why and when they should optimize, as premature and unnecessary optimization can lead to issues.
Also nowadays, machines are really powerful and interpreters and compilers are really smart and know how to optimize accordingly. This being said, there are always ways and places to optimize your code.
Check this thread out, if you are interested in learning more about premature optimization.
thanks for the valuable comment @Manolo Edge..
No problem, thanks to you for the post :)
Nice post.
It would be nice if throttle function itself is more pure (i.e. affects no global), so that I can copypasta it. Or probably globals be a Map / WeakMap that keep track of things?
Or link to some nice resources.
That's sounds good dude..Will update it as a pure function..
Nice article!
I've just posted a comparison of debounce vs throttle vs audit vs sample techniques in RxJS
RxJS debounce vs throttle vs audit vs sample — Difference You Should Know
Kostia Palchyk ・ Jun 19 ・ 2 min read
RxJS (as well as other stream-based libraries) will also help you a lot with heavy load related optimizations and will manage nicely error handling and resource freeing.
GL
Nice post keep writing.
Well, some apps need debouncing ;-))
Yeah..will describe in it my next article...