Javascript setInterval定时会慢慢偏离保持准确的方向
似乎当IJavascript setInterval定时会慢慢偏离保持准确的方向,javascript,setinterval,Javascript,Setinterval,似乎当IsetInterval持续1000ms时,它实际上每隔1001ms左右启动一次函数。这导致运行时间越长,时间漂移越慢 var start; var f = function() { if (!start) start = new Date().getTime(); var diff = new Date().getTime() - start; var drift = diff % 1000; $('<li>').text(drift + &q
setInterval
持续1000ms时,它实际上每隔1001ms左右启动一次函数。这导致运行时间越长,时间漂移越慢
var start;
var f = function() {
if (!start) start = new Date().getTime();
var diff = new Date().getTime() - start;
var drift = diff % 1000;
$('<li>').text(drift + "ms").appendTo('#results');
};
setInterval(f, 1000);
var启动;
var f=函数(){
如果(!start)start=new Date().getTime();
var diff=new Date().getTime()-start;
var漂移=差异%1000;
$(“”).text(漂移+ms”).appendTo(“#结果”);
};
设定间隔(f,1000);
运行时,立即显示不准确
- 0毫秒
- 1ms
- 2ms
- 3ms
- 4ms
- 5ms
- 5ms
- 7毫秒
- 8毫秒
- 9ms
- 9ms
- 10毫秒
那么有没有更准确的方法来计时呢?或者一种使
setInterval
的行为更准确的方法?通过谷歌搜索,你会发现setInterval
和settimeout
都不会在你告诉它的确切指定时间执行代码。具有setInterval(f,1000)代码>它将在执行前至少等待1000毫秒,而不是完全等待1000毫秒。其他进程也在等待轮到它们使用CPU,这会导致延迟。如果你需要一个精确的计时器,计时时间为1秒。我会使用较短的时间间隔,比如50毫秒,并将其与开始时间进行比较。但我不会低于50毫秒,因为浏览器有一个最小间隔
以下是一些参考资料:
“为了了解计时器如何在内部工作,有一个重要的概念需要探讨:计时器延迟不保证。因为浏览器中的所有JavaScript都在单线程异步事件(如鼠标单击和计时器)上执行只有在执行过程中出现漏洞时才会运行。最好用图表来说明这一点,如下图所示:“摘自:
Chrome和Chrome提供了平均超过41毫秒的时间间隔,这足以让第二个时钟在一分钟内明显变慢。Safari的速度略低于41毫秒,性能优于Chrome,但仍然不太好。我在Windows XP下读取了这些数据,但Chrome在Windows 7下的性能实际上更差,平均间隔约为46毫秒
我看不到像脚本所报告的那样大的漂移:
我正在运行该脚本。现在(Chrome,Win 7)我看到:
240.005s中的240次呼叫等于0.99979次呼叫/秒
事实上,我已经看到漂移上升到.007s,然后下降到.003s。我认为你的测量技术是有缺陷的
在Firefox中,我看到它漂移得更厉害(+/-8ms),然后在下一次运行中进行补偿。大多数时候,我在Firefox中看到“每秒1.000000次呼叫”。我想我可能已经找到了解决方案。我想,如果你能测量它,你就可以补偿它,对吗
因此,如果它比应该的时间晚1毫秒、2毫秒甚至10毫秒被调用,那么下一次调用计划将对此进行补偿。只要不准确度仅限于每次调用,但时钟永远不会丢失时间,那么这应该可以很好地工作
现在我把它包装成了一个全局accurateInterval
函数,它几乎可以替代setInterval
这里是另一个自动校正间隔。这个间隔被设置为一个较短的时间段,然后它会等到至少一秒钟后才触发。它不会总是在1000毫秒后触发(似乎在0-6ms延迟范围内),但它会自动校正,不会漂移
编辑:
更新为使用调用setTimeout
而不是setInterval
,否则在大约1000次迭代后,它可能会做一些奇怪的事情
var start, tick = 0;
var f = function() {
if (!start) start = new Date().getTime();
var now = new Date().getTime();
if (now < start + tick*1000) {
setTimeout(f, 0);
} else {
tick++;
var diff = now - start;
var drift = diff % 1000;
$('<li>').text(drift + "ms").appendTo('#results');
setTimeout(f, 990);
}
};
setTimeout(f, 990);
var开始,勾选=0;
var f=函数(){
如果(!start)start=new Date().getTime();
var now=new Date().getTime();
如果(现在<开始+勾选*1000){
setTimeout(f,0);
}否则{
tick++;
var diff=现在-开始;
var漂移=差异%1000;
$(“”).text(漂移+ms”).appendTo(“#结果”);
设置超时(f,990);
}
};
设置超时(f,990);
您可以使用此函数使呼叫保持接近预期的计划。它使用setTimeout
并根据经过的时间计算下一次呼叫时间
函数setApproxInterval(回调,间隔){
让运行=真
const startTime=Date.now()
常量循环=(n次运行)=>{
const targetTime=n次运行*间隔+开始时间
const timeout=targetTime-Date.now()
设置超时(()=>{
如果(正在运行){
回调函数()
循环(n次运行+1)
}
},超时)
}
环路(1)
return()=>running=false
}
函数ClearApproxinInterval(停止间隔){
停止间隔()
}
//示例用法
const testStart=Date.now()
const interval=setApproxInterval(()=>console.log(`${Date.now()-testStart}ms`),1000)
setTimeout(()=>ClearApproxinInterval(interval),10000)
通过更改Google Chrome上的选项卡,可以轻松重现setInterval或setTimeout中的不精确性。
为了处理这些情况,您可能需要考虑在用户处于另一个选项卡时设置一个条件
setTimeout(function() {
if (!document.hasFocus()) {
//... do something different, because more than 1 second might have passed
}
}, 1000);
您可以使用“setTimeout()"处理程序显式重置其下一个间隔的方法,在错误中计算,但实际上你不能依赖浏览器中的精确性。我想知道,这里的用例是什么。有趣的观察javascript的解释性质,加上浏览器的差异,将使这一级别的精确性变得困难。你可以测试函数体运行时间过长会增加你的漂移。在安排下一个函数之前,setInterval
是否会考虑函数中经过的时间?相关问题50ms是20fps;Chrome caps回调是200fps,或5ms;Firefox和IE的运行速度至少会达到250fps(我不确定它们是否受到限制).很有趣,但超高的滴答声率似乎是一个强力解决方案
var start, tick = 0;
var f = function() {
if (!start) start = new Date().getTime();
var now = new Date().getTime();
if (now < start + tick*1000) {
setTimeout(f, 0);
} else {
tick++;
var diff = now - start;
var drift = diff % 1000;
$('<li>').text(drift + "ms").appendTo('#results');
setTimeout(f, 990);
}
};
setTimeout(f, 990);
setTimeout(function() {
if (!document.hasFocus()) {
//... do something different, because more than 1 second might have passed
}
}, 1000);