不使用 Date.getTime() 实现 JavaScript 秒表

3

作为一名 JavaScript 初学者,我想尝试编写秒表代码,我写了以下内容:

<!DOCTYPE html>
<html>
<body>
    <p>A script on this page starts a stopwatch:</p>
    <p id="demo"></p>

    <button id="start-stop" onclick="myTimerFunction()">Start time</button>
    <button id="resetter" style="visibility:hidden" onclick="resetTimer()">Reset</button>

    <script>
        var timer = new Object();
        timer.hours = 0;
        timer.minutes = 0;
        timer.seconds = 0;
        timer.milliseconds = 0;
        timer.add = add;
        function add() {
            timer.milliseconds+=10;
            if(timer.milliseconds == 1000) {
                timer.seconds++;
                timer.milliseconds = 0;
            }
            if(timer.seconds == 60) {
                timer.minutes++;
                timer.seconds = 0;
            }
            if(timer.minutes == 60) {
                timer.hours++;
                timer.minutes = 0;
            }
        }
        timer.display = display;
        function display () {
            var str = "";
            if(timer.hours<10) {
                str += "0";
            }
            str += timer.hours;
            str += ":";
            if(timer.minutes<10) {
                str += "0";
            }
            str += timer.minutes;
            str += ":";
            if(timer.seconds<10) {
                str += "0";
            }
            str += timer.seconds;
            str += ":";
            /*var x = timer.milliseconds/10;
            if(x < 10) {
                str += "0";
            }*/
            if(timer.milliseconds<10) {
                str += "0";
            }
            if(timer.milliseconds<100) {
                str += "0";
            }
            str += timer.milliseconds;
            return str;
        }
        timer.reset = reset;
        function reset() {
            timer.hours = 0;
            timer.minutes = 0;
            timer.seconds = 0;
            timer.milliseconds = 0;
        }

        var myVar;
        function start() {
            timer.add();
            var d = new Date();
            var t = d.toLocaleTimeString();
            document.getElementById("demo").innerHTML = timer.display() + "\t" + t;
        }

        function stop() {
            clearInterval(myVar);
        }

        function resetTimer() {
            stop();
            timer.reset();
            document.getElementById("demo").innerHTML = timer.display();
            document.getElementById("start-stop").innerHTML="Start time";
            document.getElementById("resetter").style.visibility="hidden";
        }

        function myTimerFunction() {
            var x = document.getElementById("start-stop");
            if(x.innerHTML.match("Start time")) {
                document.getElementById("resetter").style.visibility="visible";
                myVar = setInterval(function(){start()},10);
                x.innerHTML="Stop time";
            }
            else if(x.innerHTML.match("Stop time")) {
                stop();
                x.innerHTML="Start time";
            }
        }
    </script>
</body>
</html>

但是,当我把延迟(delay)设为1并做相应的更改时,在计算秒数时它给出的时间比正常时钟慢。只有在延迟(delay)>=10的情况下才能提供“有点”可靠的时间。
我在线上检查了一些秒表js脚本,但它们都使用某种形式的Date(),并将"delay"设置为"50",目前我不明白为什么要这样做。这里在SO上有一个答案,它没有使用Date(),但它也有和我的问题一样的问题。由于我没有足够的声望来评论那里,所以我正在提问。
所以我的问题是:如果我们不使用Date()函数,是否不可能实现正常的时钟准确性?否则,如果可能的话,请帮助我改进这段代码或提供一些指针。
谢谢。

你不想使用 getTime 是因为什么? - Jonathan
我感觉我正在依赖一个已经可用的计数器。由于我正在实现一个计数器,我感觉这不是我应该选择的方式,而是其他方式。但是,现在我感觉自己正在重新发明轮子。 - abhilash
当你点击Fiddle时,下面的我的回答提供了你正在尝试的示例。对于较长时间段,getTime更为合适/准确。 - Jonathan
是的,看起来是这样。我应该做更多的研究。谢谢你的建议 :) - abhilash
3个回答

2

以下是不使用 getTime 的方法,但实际上你真的不应该这样做...

var ms = 0;
var intervalID;

function start() {
    var freq = 10; // ms
    intervalID = setInterval(function () {
        ms += 10;
        var myDate = new Date(ms);
        document.getElementById('watch').innerHTML = myDate.getUTCHours() + ":" + myDate.getMinutes() + ":" + myDate.getSeconds() +
            ":" + myDate.getMilliseconds();
    }, freq);
}

function stop() {
    clearInterval(intervalID);
}

function reset() {
    ms = 0;
    myDate = new Date(ms);
    document.getElementById('watch').innerHTML = myDate.getUTCHours() + ":" + myDate.getMinutes() + ":" + myDate.getSeconds() +
        ":" + myDate.getMilliseconds();
}

Fiddle


1

正如您已经发现的那样,setInterval/setTimeout不可靠。您必须使用本地时间库才能获得可靠的时间。

由于无法在JavaScript中保持时间,所以想法是轮询时间,并经常轮询,以使其看起来足够接近。

如果您天真地执行以下操作:

setInterval(function () {
    console.log((new Date()).getTime();
}, 1000); // 1 second

你会发现它会跳过秒数。
更好的做法是这样的:
var last = 0;
setInterval(function () {
    var now = Math.floor((new Date()).getTime() / 1000); // now in seconds
    if (now !== last) {
        console.log(now);
        last = now;
    }
}, 10); // 10ms

为什么你要每10毫秒Math.floor秒数? - Jonathan
我想你可以使用 if (last >= now - 1) { 来代替。这样可能更好。 - Halcyon
@Doge 我认为应该是 if (last <= now - 1),因为当 now 是一个很大的数时,last 是 _0_。 - abhilash

1

谢谢,这是一篇很好的文章。现在我明白了,我不应该过度依赖 setInterval 的“延迟”,而是应该给它足够的时间来恢复。 - abhilash

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