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

Creating own throttle function and testing with setTimeout

本文关键字:setTimeout 测试 自己的 功能 创建      更新时间:2023-09-26

我有一个任务要编写我自己的throttle函数。它需要使用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进行测试是否存在任何明显的问题?

您的代码中有什么问题:

  1. setIntervalsetTimeout相比计算量较大
  2. 你的测试在单线程上运行,当部署代码时会是这样吗?-不,我想不是。您可以记录函数被调用的确切时间以及由哪个线程调用
  3. 当你有高负载或几个线程检查它们是否应该执行时,我认为你会经历时间膨胀

仅仅因为您将其设置为以1ms的间隔运行并不意味着浏览器就可以做到这一点。例如,如果我将其设置为0,以强制它采用尽可能小的间隔,并多次这样做以获得平均值,我会发现我可以使用的最小间隔为~6毫秒。在重负载的情况下,这会显著增加。

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>

它是如何真正做到的

它不算自己写,而是注释下划线源节流函数:

返回一个函数,该函数在被调用时最多只会被触发在给定的时间窗口内一次。正常情况下,节流功能将尽可能多地运行,每次等待不会超过一次期间但是如果您想禁用前导上的执行edge,pass{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:false}。

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

您需要清除间隔,因为无论您将间隔设置为零,它们仍然会递增计数器,并且如果您想调用其他函数,而不仅仅是一个集false calledOnce。

也许这个代码可以帮助你。


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

这将起作用:

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个"挂起"变量。("挂起"变量是一个从不被垃圾收集的变量,因为它总是被可能被调用的函数引用,从而占用内存。)请注意。

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"的键,并将其设置为所需的毫秒数。