我有一个任务要编写我自己的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进行测试是否存在任何明显的问题?
您的代码中有什么问题:
- setInterval与setTimeout相比计算量较大
- 你的测试在单线程上运行,当部署代码时会是这样吗?-不,我想不是。您可以记录函数被调用的确切时间以及由哪个线程调用
- 当你有高负载或几个线程检查它们是否应该执行时,我认为你会经历时间膨胀
仅仅因为您将其设置为以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"的键,并将其设置为所需的毫秒数。