有没有比 setTimeout 更准确的方法来创建 Javascript 计时器?

IT技术 javascript timer
2021-01-13 17:25:01

一直困扰着我的事情是setTimeout()Javascript 中方法是多么不可预测

根据我的经验,计时器在很多情况下都非常不准确。不准确,我的意思是实际延迟时间似乎或多或少有 250-500 毫秒的变化。虽然这不是很长的时间,但是当使用它来隐藏/显示 UI 元素时,时间会很明显。

是否有任何技巧可以确保setTimeout()准确执行(无需求助于外部 API),或者这是一个失败的原因?

6个回答

是否有任何技巧可以确保setTimeout()准确执行(无需求助于外部 API),或者这是一个失败的原因?

没有也没有。你不会得到任何接近完美准确的计时器setTimeout()- 浏览器没有为此设置。但是,您也不需要依赖它来计时。大多数动画库在几年前就发现了这一点:您使用 设置回调setTimeout(),但根据 的值(new Date()).milliseconds(或等效确定需要执行的操作这使您可以在较新的浏览器中利用更可靠的计时器支持,同时在较旧的浏览器上仍能正常运行。

它还可以让您避免使用太多计时器这很重要:每个计时器都是一个回调。每个回调都执行 JS 代码。当 JS 代码正在执行时,浏览器事件——包括其他回调——被延迟或丢弃。当回调完成时,额外的回调必须与其他浏览器事件竞争执行的机会。因此,一个处理该时间间隔内所有待处理任务的计时器将比两个具有重合时间间隔的计时器执行得更好,并且(对于短超时)比具有重叠超时的两个计时器更好!

总结:停止使用setTimeout()实现“一个定时器/一个任务”的设计,并使用实时时钟来平滑UI动作。

如何从工作人员调用 setTimeout() 。不会减少渲染造成的阻塞吗?
2021-03-11 17:25:01
requestAnimationFrame 提供更高的精度,但不一定提供更好的准确性@StijndeWitt-您仍然受浏览器的支配,浏览器可能会因其他处理甚至您在回调中实现时所做的任何选择而停滞。Chris 的技术很好,但是如果您希望确保逻辑尽可能接近设定的时间执行,您仍然需要根据检索到的时间值来决定到期时间;requestAnimationFrame 提供的关于何时调用它的保证甚至比 setTimeout 还要少。另请参阅我之前对 hidarikani 的注释。
2021-03-12 17:25:01
我认为这个答案是错的,现在实际上,自从我们有了requestAnimationFrame现在......又见此答案克里斯这不是一个简单的解决方案,但它满足 OP 没有外部 API 的标准。
2021-03-23 17:25:01
不能保证这一点,@hidarikani。FWIW,这可能是本机代码中的一个问题,而在浏览器中,您没有能力要求高分辨率计时器和实时延迟。不是你想要的;这是浪费 CPU 的秘诀,您应该假设至少在某些情况下,浏览器和操作系统将尽其所能来延长电池寿命并为该目标牺牲计时器的准确性。
2021-03-31 17:25:01
“requestAnimationFrame 提供的保证甚至更少” 这是一个很好的观点!谢谢
2021-04-01 17:25:01

.

参考;http://www.sitepoint.com/creating-accurate-timers-in-javascript/

这个网站在很大程度上拯救了我。

您可以使用系统时钟来补偿计时器的不准确性。如果您将计时函数作为一系列 setTimeout 调用运行 - 每个实例调用下一个 - 那么您需要做的就是确保它准确无误,并从下一次迭代中减去该差异:

var start = new Date().getTime(),  
    time = 0,  
    elapsed = '0.0';  
function instance()  
{  
    time += 100;  
    elapsed = Math.floor(time / 100) / 10;  
    if(Math.round(elapsed) == elapsed) { elapsed += '.0'; }  
    document.title = elapsed;  
    var diff = (new Date().getTime() - start) - time;  
    window.setTimeout(instance, (100 - diff));  
}  
window.setTimeout(instance, 100);  

这种方法将最大限度地减少漂移并将不准确率降低 90% 以上。

解决了我的问题,希望有帮助

@Mr_Chimp,为什么elapsed您的代码中需要变量?
2021-03-12 17:25:01
doTimer那篇文章末尾功能正是我所需要的。它超级准确。
2021-03-13 17:25:01
@AntonRudeshko 嗯……好问题。我的代码改编自上面的链接。认为这只是一个无用的剩菜
2021-03-28 17:25:01

不久前我遇到了类似的问题,并提出了一种requestAnimationFrame与 performance.now()结合使用的方法,该方法非常有效。

我现在能够使计时器精确到大约 12 位小数:

    window.performance = window.performance || {};
    performance.now = (function() {
        return performance.now       ||
            performance.mozNow    ||
            performance.msNow     ||
            performance.oNow      ||
            performance.webkitNow ||
                function() {
                    //Doh! Crap browser!
                    return new Date().getTime(); 
                };
        })();

http://jsfiddle.net/CGWGreen/9pg9L/

如果您需要在给定的时间间隔内获得准确的回调,此要点可能会帮助您:

https://gist.github.com/1185904

function interval(duration, fn){
  var _this = this
  this.baseline = undefined
  
  this.run = function(){
    if(_this.baseline === undefined){
      _this.baseline = new Date().getTime()
    }
    fn()
    var end = new Date().getTime()
    _this.baseline += duration
 
    var nextTick = duration - (end - _this.baseline)
    if(nextTick<0){
      nextTick = 0
    }
    
    _this.timer = setTimeout(function(){
      _this.run(end)
    }, nextTick)
  }

  this.stop = function(){
    clearTimeout(_this.timer)
  }
}

shog9 的回答几乎就是我想说的,尽管我会添加以下关于 UI 动画/事件的内容:

如果你有一个盒子,它应该滑到屏幕上,向下扩展,然后淡入它的内容,不要试图让所有三个事件分开,延迟时间让它们一个接一个地触发 - 使用回调,所以一次第一个事件完成滑动它调用扩展器,一旦完成它调用推子。jQuery 可以轻松完成,我相信其他库也可以。