javascriptjquerythrottling

Simple throttle in JavaScript


I am looking for a simple throttle in JavaScript. I know libraries like lodash and underscore have it, but only for one function it will be overkill to include any of those libraries.

I was also checking if jQuery has a similar function - could not find.

I have found one working throttle, and here is the code:

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);
    }
  };
}

The problem with this is: it fires the function once more after the throttle time is complete. So let's assume I made a throttle that fires every 10 seconds on keypress - if I do keypress 2 times, it will still fire the second keypress when 10 seconds are completed. I do not want this behavior.


Solution

  • I would use the underscore.js or lodash source code to find a well tested version of this function.

    Here is the slightly modified version of the underscore code to remove all references to underscore.js itself:

    // Returns a function, that, when invoked, will only be triggered at most once
    // during a given window of time. Normally, the throttled function will run
    // as much as it can, without ever going more than once per `wait` duration;
    // but if you'd like to disable the execution on the leading edge, pass
    // `{leading: false}`. To disable execution on the trailing edge, ditto.
    function throttle(func, wait, options) {
      var context, args, result;
      var timeout = null;
      var previous = 0;
      if (!options) options = {};
      var later = function() {
        previous = options.leading === false ? 0 : Date.now();
        timeout = null;
        result = func.apply(context, args);
        if (!timeout) context = args = null;
      };
      return function() {
        var now = Date.now();
        if (!previous && options.leading === false) previous = now;
        var remaining = wait - (now - previous);
        context = this;
        args = arguments;
        if (remaining <= 0 || remaining > wait) {
          if (timeout) {
            clearTimeout(timeout);
            timeout = null;
          }
          previous = now;
          result = func.apply(context, args);
          if (!timeout) context = args = null;
        } else if (!timeout && options.trailing !== false) {
          timeout = setTimeout(later, remaining);
        }
        return result;
      };
    };
    

    Please note that this code can be simplified if you don't need all the options that underscore support.

    Please find below a very simple and non-configurable version of this function:

    function throttle (callback, limit) {
        var waiting = false;                      // Initially, we're not waiting
        return function () {                      // We return a throttled function
            if (!waiting) {                       // If we're not waiting
                callback.apply(this, arguments);  // Execute users function
                waiting = true;                   // Prevent future invocations
                setTimeout(function () {          // After a period of time
                    waiting = false;              // And allow future invocations
                }, limit);
            }
        }
    }
    

    Edit 1: Removed another reference to underscore, thx to @Zettam 's comment

    Edit 2: Added suggestion about lodash and possible code simplification, thx to @lolzery @wowzery 's comment

    Edit 3: Due to popular requests, I added a very simple, non-configurable version of the function, adapted from @vsync 's comment