每毫秒运行一个函数

10

我想每毫秒运行一次函数,为了实现这个目的,我只使用了javascript中的setInterval概念。我的代码如下所示,

HTML:

<div id=test>0.0</div>

脚本:

var xVal = 0;
var xElement = null;

xElement = document.getElementById("test");
var Interval = window.setInterval(startWatch, 1);


function startWatch(){

    xVal += 1;
    xElement.innerHTML = xVal;

}

所以上面的代码运行正常。但是当我用真实的时钟测试结果时,真实的时钟需要1000毫秒才能完成1秒,而同时结果需要超过1000毫秒才能完成一秒。

演示

有人能告诉我吗,

我的代码有什么错误吗?如果有,那么告诉我如何精确显示毫秒。


4
浏览器不是实时系统,任何JS代码段的执行时间可能会超过1毫秒。使用1毫秒间隔并不是一个好的解决方案。 - Matt S
6个回答

9

您的代码没有错误,但JavaScript计时器(setIntervalsetTimeout不够精确。浏览器无法遵守这么短的间隔时间。因此,我很抱歉在Web浏览器上没有一种精确地每次递增一毫秒并显示更新的方式。不管怎样,这甚至对人眼来说都不可见!

一个精确的解决方法将涉及较大的间隔时间和时间戳以计算以毫秒为单位的经过时间:

var start = new Date().getTime();
setInterval(function() {
    var now = new Date().getTime();
    xElement.innerHTML = (now - start) + 'ms elapsed';
}, 40);

如果我想模拟一个秒表,那么在Web应用程序中这是不可能的,对吧? - Rajaprabhu Aravindasamy
1
看看我的更新。这是可行的,不可能使用1毫秒增量并期望它在视觉上更新。它甚至对人眼也不可见。 - bfavaretto

6

4

实际上,DOM无法每秒更新1000次。事实上,您的显示器甚至无法在一秒钟内显示1000帧。在函数中计算开始时间和当前时间之间的毫秒差,并使用它:

(function(){
    var xElement = document.getElementById("test");
    var start = new Date;

    (function update(){
      xElement.innerHTML = (new Date - start);

      setTimeout(update, 0);
    })();
}();

Updated fiddle


使用递归的 setTimeout 而不是 setInterval,加一分。 - War10ck

3

由于HTML的渲染和时间间隔的运行延迟,您无法使用您的方法来完成此操作。通过这种方式,大约可以以60FPS正确显示时间。

http://jsfiddle.net/3hEs4/3/

var xElement = null;
var startTime = new Date();

xElement = document.getElementById("test");
var Interval = window.setInterval(startWatch, 17);


function startWatch(){
     var currentTime = new Date();
     xElement.innerHTML = currentTime - startTime;   
}

你可能想要考虑使用requestanimationframe而不是像那样使用硬编码的setInterval。


1
硬编码17的原因是什么? - Rajaprabhu Aravindasamy
是的,60FPS之间的帧间时间约为1000/60=16.67ms。四舍五入到17。 - PherricOxide
你可以使用1000/60代替硬编码的17作为参数 :) - Luca Rainone

0

我曾经有同样的问题,但找不到任何可行的解决方案,所以我自己创建了一个。下面的代码基本上每5毫秒调用五个setTimout,对于5到10之间的每个毫秒都是如此。这可以规避最小4毫秒的限制,并且(在Firefox、Chrome和Opera中进行了检查)运行得相当不错。

const start = performance.now();
let newNow = 0;
let oldNow = 0;

const runner = function(reset) {
    // whatever is here will run ca. every ms
    newNow = performance.now();
    console.log("new:", newNow);
    console.log("            diff:", newNow - oldNow);
    oldNow = newNow
    if (newNow - start < 1000 && reset) {
        setTimeout(function() {
            runner(true);
        }, 5);
        for (let i = 6; i < 11; i++) {
            setTimeout(function() {
                runner(false);
            }, i);
        }
    }
};
runner(true);

当然,它可以更优雅地编写,例如,您可以更轻松地自定义刻度(例如0.5毫秒或2毫秒而不是1毫秒),但无论如何原则都在那里。

我知道理论上你可以调用5个setInterval,但实际上这会导致漂移,很快就会破坏毫秒精度。

还要注意,有合法的使用情况。(例如,我需要持续测量触摸力,否则无法实现。)


0

setInterval 回调函数可能不会以毫秒精度发生,因为计时器运行的线程在时间到达时可能甚至没有实际运行,或者浏览器限制事件,或者其他很多事情。

此外,由于大多数 JavaScript 引擎都是单线程的,setInterval 的实现可能会在触发后运行您的回调,然后为下一次调用重置时钟。由于您正在进行一些 DOM 操作,这本身可能需要几毫秒。

简而言之,您期望从另一个应用程序内部运行的解释器中获得实时操作系统行为,这很可能不是实时操作系统。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接