JS 前端倒计时时间不准确如何解决?

2021-08-09 09:18:40 +08:00
 CSGO

我在写一个简单的页面,有 3 个倒计时,在别人的帮助下,成功实现了功能,但是发现 js 代码在不同浏览器下,倒计时都有误差,大约每 60 秒就会慢 1 秒钟左右,有没什么办法修复它?

function setNumber(elem, num) {
    elem.innerText = num.toString().padStart(2, '0');
}

function countdown(elem, init) {
    setNumber(elem, init);
    return setInterval(() => {
        let num = Number(elem.innerText);
        let next = num - 1;
        if (next < 0) next = init;
        setNumber(elem, next);
    }, 1000);
}
function init() {
    var timeList = [
        { 'name': 'explode-id', 'node': 'wifi-explode', 'time': 60 },
        { 'name': 'nokit-id', 'node': 'wifi-nokit', 'time': 30 },
        { 'name': 'ownkit-id', 'node': 'wifi-ownkit', 'time': 35 }
    ];
    timeList.forEach(function (item) {
        clearInterval(parseInt(document.getElementById(item['name']).innerHTML));
        document.getElementById(item['name']).innerHTML = countdown(document.getElementsByClassName(item['node'])[0], item['time']);
    });
}
let wifiClick = document.getElementsByClassName('wifi-click')[0];
init();
wifiClick.addEventListener('click', () => {
    init();
})
6053 次点击
所在节点    问与答
54 条回复
Jackliu
2021-08-09 09:22:43 +08:00
隔段时间网络校准一次?前提网络良好
lingo
2021-08-09 09:23:43 +08:00
每秒跟本地时间对比一次也好。
CSGO
2021-08-09 09:27:02 +08:00
@lingo 好写吗?本地就行,它就是个本地计时器,没必要网络对时。好写的话,我花小钱请朋友写。我不懂代码。
codehz
2021-08-09 09:29:59 +08:00
这思路就错了,应该记录开始时间,然后每次计算流逝的时间,再计算出剩余时间)
des
2021-08-09 09:35:16 +08:00
又一个拿 setInterval 次数用来计时的,你可以试着把你这个 tab 切到后台放一阵,那样就更不准了
正确应该算时间差
bnm965321
2021-08-09 09:39:47 +08:00
因为 JavaScript 一般的运行时是单线程的,可能其它任务正在执行中,不能保证一定在 interval 之后执行这个任务
wtf12138
2021-08-09 09:44:53 +08:00
每 100 毫秒 getTime 一下怎么样,就是好像有点浪费
murmur
2021-08-09 09:46:41 +08:00
不要自己去计算时间,用系统的基准时间去对比,你可以半秒刷一次,但是和初始时间+new Date 比
lingo
2021-08-09 09:46:41 +08:00
你是要在开始记录之后分别计时 30 、35 、60 秒嘛。
那就记录下开始时间的时间戳,分别加上 30 、35 、60 秒的时间,得到三个未来的时间点。
每秒判断当前的系统时间是否大于那三个时间点。这样只能保证不管过了多长时间,都不会因为 js 的计时导致误差大于 1 秒。。。
clino
2021-08-09 09:47:48 +08:00
时间差+1
xiaojun1994
2021-08-09 09:47:54 +08:00
不应该去++或者--,应该一开始计算截止时间,每次用截止时间来计算
acthtml
2021-08-09 09:49:20 +08:00
系统计算会消耗时间,所以你每个 interval 其实是 1000ms + 系统消耗。在有些系统的浏览器切换 tab 还会挂起程序,这样的话误差更大。

可以改成每个 interval 实时计算:剩余时间 = 目标时间 - 当前时间
CSGO
2021-08-09 09:51:00 +08:00
@acthtml 明白了,临时解决方案修改 1000 为 980 之类的。
CSGO
2021-08-09 09:52:00 +08:00
@lingo 好,原理我知道了,谢谢啊。
zhaol
2021-08-09 09:57:38 +08:00
js 的 setInterval 并不能保证一定是 1000ms 的时候就立即执行,有时候可能因为其他的事件,导致阻塞,然后延迟
wenzichel
2021-08-09 09:58:01 +08:00
你不能直接拿上一个数据来计算。而是每次都获取当前的时间与截止时间的差值来显示倒计时。

这样不会因为其他代码的执行,导致计时不准确,因为每次都会拿当前时间进行校准。

const lastTime = '2021/12/31 23:59:59';

setInterval(() => {
const diff = Date.now() - new Date(lastTime).getTime();
setNumber( formatTime(diff) ); // formatTime()是将时间戳转为时分秒的方法,请自行实现
}, 1000);
Sapp
2021-08-09 10:06:09 +08:00
@CSGO 你这个 980 也依旧不准时,正确的就是记录上次的时间,然后实行的时候获取当前时间,两个相减,再做一些处理(解决切换其他 tab 被暂停的情况),取出一个精确地剩余时间,然后再设置为新的定时器时间,这样应该能准一些,但是如果有其他任务阻塞可能还是不准的,依赖这个准时去做任务本来就不靠谱
CSGO
2021-08-09 10:08:56 +08:00
@Sapp 其实原理我明白,主要是 js 居然有误差,以及因为我不是开发者,我做这个就是一个用于 CSGO 的自用和分享朋友用的一个 c4 炸弹计时器。目前来看我的能力只能计算它误差大约多少,然后把 1000ms 修改成类似 980ms 之类的,因为我只需要大约 40 秒一轮回误差不要太大即可。
ipwx
2021-08-09 10:12:12 +08:00
@CSGO 这不是误差,这是设计。

你把页面 tab 放后台一段时间,setInterval 就会停下来不会继续运行的。
des
2021-08-09 10:23:17 +08:00
@CSGO 要说的话,不只 js 有误差,所有语言都可能出现误差

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/794514

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX