Search code examples
javascripttimeoutthrottling

Creating own throttle function and testing with setTimeout


I have an assignment to write my own throttle function. It needs to pass a certain amount of tests using setTimeout.

This is my code:

var throttle = function(func, delay) {
  var counter = 0;
  var calledOnce = false;
  setInterval(function(){
    counter++;
  }, 1);
  return function() {
    if (counter > delay || !calledOnce) {
      calledOnce = true;
      counter = 0; 
      return func.apply(this, arguments);
    }
  };
};

I am testing it with the following:

var callBack = function () {
  console.log('called');
};

var func = throttle(callback, 1000);

func();                     // should be called
setTimeout(func, 500);      // should not be called
setTimeout(func, 1000);     // should be called
setTimeout(func, 1500);     // should not be called
setTimeout(func, 1900);     // should not be called

However, when I run my code just how it is here, the function is only invoked once, with the original func() call, and none of the functions inside of a setTimeout are being invoked.

Is there any obvious problems with my code or testing using setTimeout?


Solution

  • What is the problem in your code:

    1. setInterval is computationally heavy compared to setTimeout.
    2. your tests run on single thread will that be the case when code is deployed ? - no, i think not. You can log exact time when function is called and by what thread.
    3. when you have high load or several threads checking if they should execute then I think you will experience time dilation.

    Just because you set it to run at 1ms intervals does not mean, that browser does this. For example if i set it to 0 to force it to take smallest possible interval and do this several times to get average i find it smallest interval i could use is ~6 ms. In case of heavy load this increases significantly.

    var start = new Date();
    var i = 0, interval = setInterval(function(){
        if (++i >= 1000) {
            var end = new Date();
            var result = (end-start)/1000;
            $('#result').text("The average interval was "
                              +result+" milliseconds");
            $('#browser').text(navigator.userAgent);
            clearInterval(interval);
        }
    }, 0);
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
    <p id=result>Please wait about 10 to 20 seconds ...</p>
    <p id=browser></p>

    How it is really done

    It does not count as writing it yourself, but annotated underscore source throttle function :

    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.

      _.throttle = function(func, wait, options) {
        var context, args, result;
        var timeout = null;
        var previous = 0;
        if (!options) options = {};
        var later = function() {
          previous = options.leading === false ? 0 : _.now();
          timeout = null;
          result = func.apply(context, args);
          if (!timeout) 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 || 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;
        };
      };
    

    From http://underscorejs.org/#throttle

    throttle_.throttle(function, wait, [options]) 
    

    Creates and returns a new, throttled version of the passed function, that, when invoked repeatedly, will only actually call the original function at most once per every wait milliseconds. Useful for rate-limiting events that occur faster than you can keep up with.

    By default, throttle will execute the function as soon as you call it for the first time, and, if you call it again any number of times during the wait period, as soon as that period is over. If you'd like to disable the leading-edge call, pass {leading: false}, and if you'd like to disable the execution on the trailing-edge, pass {trailing: false}.

    var throttled = _.throttle(updatePosition, 100);
    $(window).scroll(throttled);