创建自己的油门功能并使用setTimeout进行测试

时间:2015-11-13 07:34:07

标签: javascript timeout throttling

我有一个编写自己的油门功能的任务。它需要使用setTimeout传递一定量的测试。

这是我的代码:

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

我正在测试它:

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

但是,当我运行我的代码时,它只是调用一次,使用原始的func()调用,并且没有调用setTimeout中的任何函数。

我的代码或使用setTimeout进行测试是否有明显问题?

4 个答案:

答案 0 :(得分:2)

您的代码中有什么问题:

    setTimeout 相比,
  1. setInterval 的计算量很大。
  2. 在单个线程上运行的测试是否会在部署代码时出现? - 不,我想不是。您可以在调用函数时以及通过什么线程记录确切的时间。
  3. 当您有高负荷或多个线程检查它们是否应该执行时,我认为您将体验时间膨胀
  4. 仅仅因为你设置它以1毫秒间隔运行并不意味着,浏览器会这样做。例如,如果我将其设置为0以强制它采取最小可能的间隔并且这样做几次以获得平均值我发现它可以使用的最小间隔是~6 ms。在负载很重的情况下,这会显着增加。

    
    
    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>
    &#13;
    &#13;
    &#13;

    它是如何完成的

    它不算是自己写,而是annotated underscore source throttle function

      

    返回一个函数,该函数在被调用时最多只会被触发   在给定的时间窗口期间。通常,限制功能   将尽可能多地运行,每次等待不会超过一次   持续时间;但是如果你想禁用领先的执行   edge,传递{leading:false}。禁用尾随执行   边缘,同上。

      _.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;
        };
      };
    

    来自http://underscorejs.org/#throttle

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

    创建并返回传递函数的新的受限制版本,   当重复调用时,只会实际调用原始文件   每等待几毫秒最多运行一次。对...有用   速率限制事件发生得比你能跟上的速度要快。

         

    默认情况下,只要您调用它,throttle就会执行该功能   这是第一次,如果你再次多次调用它   在等待期间,一旦这段时间结束。如果你喜欢   要禁用前沿呼叫,请传递{leading:false},如果你这样做   喜欢在尾端禁用执行,传递{trailing:   假}。

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

答案 1 :(得分:0)

你需要清除间隔,因为它们仍然会增加计数器,无论你是否设置为零,间隔仍然有效,如果你想调用其他函数,不只是一个假设为callOnce。

也许这段代码可以帮到你。

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

答案 2 :(得分:0)

这将有效:

var throttle = function (input, delay) {
    var counter = 0;
    var calledOnce = false;
    var prev = Date.now();

    return function () {
        var now = Date.now();
        counter = now - prev;
        if (counter > delay || !calledOnce) {
            calledOnce = true;
            counter = 0;
            prev = now;
            return input.apply(null);
        }
    };
};

答案 3 :(得分:0)

对于合适的油门功能,人们不需要大量的局部变量。节流功能的目的是减少浏览器资源,而不是应用你正在使用的更多开销。作为这种说法的证据的证明,我设计了一种油门功能,其范围内只有3个“悬挂”变量。 (“挂起”变量是一个永远不会被垃圾收集的变量,因为它总是被一个可以潜在调用的函数引用,从而吸收内存。)请注意。

var timenow = self.performance?performance.now.bind(performance):Date.now;
function throttle(func, minInterval) {
    var lastTimeWent = -minInterval;
    return function() {
        var newTimeWent = timenow();
        if ((newTimeWent-lastTimeWent) > minInterval) {
            lastTimeWent = newTimeWent;
            return func.apply(this, arguments);
        }
    };
}

使用您的示例进行测试:

(function(){"use strict";
var timenow = self.performance?performance.now.bind(performance):Date.now;
function throttle(func, minInterval) {
    var lastTimeWent = -minInterval;
    return function() {
        var newTimeWent = timenow();
        if ((newTimeWent-lastTimeWent) > minInterval) {
            lastTimeWent = newTimeWent;
            return func.apply(this, arguments);
        }
    };
}
var callBack = function () {
    console.log('\tcalled!');
};
var func = throttle(callBack, 1000);

var start = timenow();
wrapper();                     // should be called
setTimeout(wrapper, 500);      // should not be called
setTimeout(wrapper, 1000);     // should be called
setTimeout(wrapper, 1500);     // should not be called
setTimeout(wrapper, 1900);     // should not be called
async function wrapper(){
    console.log("Calling after " + Math.round(timenow()-start) + "ms...");
    func();
    console.log("Done Calling!");
}
})();

默认情况下,此功能限制为每200ms最多一次调用。要将间隔更改为不同的毫秒数,请在options参数中传递名为“interval”的键,并将其设置为所需的毫秒数。