DEV Community

Cover image for All Javascript apps need event throttling!!!
jayakrishnan TN
jayakrishnan TN

Posted on

All Javascript apps need event throttling!!!

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;

});

RESULT:
Without throttling

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:

event-throttling

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)

Collapse
 
alaindet profile image
Alain D'Ettorre

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.

Collapse
 
nombrekeff profile image
Keff

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.

"Programmers waste enormous amounts of time thinking about, or worrying about, the speed of noncritical parts of their programs, and these attempts at efficiency actually have a strong negative impact when debugging and maintenance are considered."
Donald Knuth on "StructuredProgrammingWithGoToStat...

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.

Collapse
 
jkjaikrishna profile image
jayakrishnan TN

thanks for the valuable comment @Manolo Edge..

Collapse
 
nombrekeff profile image
Keff

No problem, thanks to you for the post :)

Collapse
 
patarapolw profile image
Pacharapol Withayasakpunt • Edited

Nice post.

the javascript script.js file is:

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.

Collapse
 
jkjaikrishna profile image
jayakrishnan TN

That's sounds good dude..Will update it as a pure function..

Collapse
 
kosich profile image
Kostia Palchyk

Nice article!

I've just posted a comparison of debounce vs throttle vs audit vs sample techniques in RxJS

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

Collapse
 
nandalal profile image
Nandalal

Nice post keep writing.

Collapse
 
pavelloz profile image
Paweł Kowalski

Well, some apps need debouncing ;-))

Collapse
 
jkjaikrishna profile image
jayakrishnan TN

Yeah..will describe in it my next article...