Throttling function calls

(edit)

If you've written any kind of validation on user input, like onkeypress then you'll know that sometimes you want to throttle the amount of times your function runs. A good example of this is Ajax based username validation - you don't want to hit the server on every key press, because most users will be able to write their name in around 1/10th of a second, so you should throttle the ajax request until the input is dormant for 100ms.

2½ years later, I decide that Ben was right - and nowadays I refer to this as a debounce rather than a throttle. I've updated this post so that the function name reflects what it does on the tin, but also add my own throttle function that fires the callback based on a specific frequency. The throttle function will also always fire the first and last message.

So with a bit of magic JavaScript making use of the ever useful closure JavaScript offers, we can create a simple method to handle this for us:

function debounce(fn, delay) {
  var timer = null;
  return function () {
    var context = this, args = arguments;
    clearTimeout(timer);
    timer = setTimeout(function () {
      fn.apply(context, args);
    }, delay);
  };
}

So if you were doing something with jQuery, like a key press validation, you would do this instead:

$('input.username').keypress(debounce(function (event) {
  // do the Ajax request
}, 250));

The keyword this is the input as you would expect, and all the correct arguments are passed to the event handle, i.e. it works the exact same way as you'd expect, except it only fires once the keypress event is idle for 250ms (in this particular case).

Below is an actual throttle function, that fires a message every 250ms by default (rather than at the end of a burst of events):

function throttle(fn, threshhold, scope) {
  threshhold || (threshhold = 250);
  var last,
      deferTimer;
  return function () {
    var context = scope || this;

    var now = +new Date,
        args = arguments;
    if (last && now < last + threshhold) {
      // hold on to it
      clearTimeout(deferTimer);
      deferTimer = setTimeout(function () {
        last = now;
        fn.apply(context, args);
      }, threshhold);
    } else {
      last = now;
      fn.apply(context, args);
    }
  };
}

So when you use this, moving the mouse around the example below, will echo out the tick on the first time you move, but then every 1 second until you stop moving the mouse:

$('body').on('mousemove', throttle(function (event) {
  console.log('tick');
}, 1000));

There's also a simple rate throttled function in the comments below, that fires on every nth message - though I'd be inclined to tweak it to ensure the first message is delivered

Comments

comments powered by Disqus