It isn’t something alien that in the discourse of web development, we’ve seen how the client has progressively been put under duress to support the application. Today, the client (the browser) is made to do considerable tasks which history considered as Herculean. JavaScript has evolved and matured in spite of its idiosyncrasies. Today, we’re going to focus on yet another elephant in the room problem related to JavaScript optimization.

Our applications today are considerably JavaScript heavy, iterative (frequent or repetitive using setInterval and setTimeout) and quite computationally active. Given these hallmarks,  we cannot connive at the fact that our applications are highly conducive to performance deterrents. I will take a very simple and popular example in this category – resize event for window resize operations. It’s frequent (well it could be) and could be computationally heavy if you have a responsive site which adjusts element positioning.

window.addEventListener(‘resize’, resizeCallback);

function resizeCallback(evt){

// my heavy and super awesome code goes here

}

Well at first glance it doesn’t seem that intimidating. However, lets add the iterative part wherein this chunk of code could kick in quite frequently. This will definitely add to the burden. The immediate and intuitive idea that comes to mind is what if we could limit the rate at which the callback is firing?

Debounce

To put it in plain English, debounce implementation will delay the invoking/execution of a function if it’s called again within (or in less than) X milliseconds. So for example, we find that our resize event is quite frequent. Then we do not want to execute the chunk of code every single time the window resizes. Rather, we will postpone it if it’s invoked again within say 300 milliseconds.

p.s: I will be referring to two major utility libraries, Underscore.js and Lodash.js. These libraries support these patterns. Readers may choose the one they like.

debounce implementation in Underscore.js is as follows:

function debounce(func, wait, immediate) {
        
var timeout;
        
return function() {
                
var context = this, args = arguments;
                 clearTimeout(timeout);
                 timeout = setTimeout(function() {
                         timeout = null;
                        
if (!immediate) func.apply(context, args);
                 }, wait);
                
if (immediate && !timeout) func.apply(context, args);
         };

};

Now, we could call debounce with our heavy, custom code. The returned function could then be passed onto the resize event callback.

var delayedResize = debounce(function() {

// my heavy and super awesome code goes here

}, 300);

window.addEventListener(‘resize’, delayedResize);

Lets dig this code up. We see that the function debounce is a closure which returns a function. delayedResize contains this returned function. Now, if we resize our window, the delayedResize function fires. Note that it immediately does NOT execute the custom code we want to execute upon resize, rather it delays it by setting up a timeout of 300 milliseconds.

timeout = setTimeout(function() {
                        
timeout = null;
                        
if (!immediate) func.apply(context, args);
                
}, wait);

This code is hence expected to execute our custom code after 300 milliseconds. However, if we are to resize our window within 300 milliseconds, we find that delayedResize first clears the timeout set:

clearTimeout(timeout);

and then it again delays the execution by re-setting the timeout. The bottom line is our custom code is executed ONLY when the delayedResize is NOT INVOKED AGAIN WITHIN 300 milliseconds. So, to say we’re waiting till the last resize event and then execute our code. This saves a lot of computational effort.

Lodash.js also has the debounce utility.

Another scenario where debounce fits in is autocomplete. So lets say our application fires up an AJAX query on the keyup event. Now, users do type fast and thus the rate at which the keyup event is fired exceeds the rate at which our backend services could serve our requests. So, it might be possible that by the time the user has typed “abina” as the search query, we’re still firing and waiting for the response of “abi”. However, with debounce we can delay the request initiation to the last keyup event fired. This will ensure consistency between the front-end and back-end and at the same time avoiding unwanted query traffic.

Throttle

The concept behind throttle is very similar to that of debounce. Most of us could immediately relate the word with the concept of throttle in a vehicle. Throttle controls how much fuel is injected into the engine over a period of time. So, translating that into the JavaScript world, throttle tries to limit our function execution over a period of time; more precisely to ONCE every K milliseconds. So, if a callback fires p times within K milliseconds, then throttle ensures that it is executed only once. Note that there’s no concept of delaying the execution here unlike in debounce. I could use the same example of window resize and apply throttle to it. Mouse events like updating position while scrolling could become tedious as they occur quite frequently and therefore should be throttled.

throttle implementation in Underscore.js:

function throttle(func, wait, options) {

    var context, args, result;

    var timeout = null;

    var previous = 0;

    options || (options = {});

    var later = function() {

      previous = options.leading === false ? 0 : _.now();

      timeout = null;

      result = func.apply(context, args);

      context = args = null;

    };

    return function() {

      var now = _.now();

      if (!previous && options.leading === false) previous = now;

     

  var remaining = wait – (now – previous);

      context = this;

      args = arguments;

      if (remaining <= 0) {

        clearTimeout(timeout);

        timeout = null;

        previous = now;

        result = func.apply(context, args);

        context = args = null;

      } else if (!timeout && options.trailing !== false) {

        timeout = setTimeout(later, remaining);

      }

      return result;

    };

};

So here we focus on the remaining variable and check if it’s less than zero i.e. the required K milliseconds have lapsed, then we execute our custom code. This guarantees that as the value of remaining diminishes and finally becomes zero (or negative) or there’s no activity within K milliseconds, only then is our custom code called.

Framework Support

Libraries like jQuery have plugins which support debounce and throttle. Underscore and Lodash already support them. Amongst the CSS frameworks, Foundation supports these patterns. I don’t think Bootstrap supports these patterns. Code snippets (taken from Foundation’s website) look like:

                             WITHOUT DELAY

  // Button click handler
   $(
‘.button’).on(‘click’, function(e){
    
// Handle Click
   });

  // Resize function
   $(window).on(
‘resize’, function(e){
    
// Do responsive stuff
   });

                                                                   WITH DELAY

   // Debounced button click handler
    $(
‘.button’).on(‘click’, Foundation.utils.debounce(function(e){
     
// Handle Click
    },
300, true));

   // Throttled resize function
    $(window).on(
‘resize’, Foundation.utils.throttle(function(e){
     
// Do responsive stuff
    },
300));

For other frameworks we could anyway write our own services as these patterns use the fundamental setTimeout within. Note that AngularJS 1.2 does support the ngDebounce directive. Others could resort to using the utility libraries mentioned earlier to utilize these patterns.

To report this post you need to login first.

Be the first to leave a comment

You must be Logged on to comment or reply to a post.

Leave a Reply