JavaScript 倒计时踩坑集锦
栏目: JavaScript · 发布时间: 7年前
内容简介:前阵子,项目中加了个倒计时的需求,接手的时候啪啪啪三声,搞定,送测某个彩笔开发:这波有bug我吃shi。
前阵子,项目中加了个倒计时的需求,接手的时候
啪啪啪三声,搞定,送测
let countdown = 100000; // ms 服务器返回的倒计时剩余时间
function startCountdown() {
setTimeout({
countdown -= 1000;
if (/* some */) {
// do some
startCountdown();
}
}, 1000);
}
复制代码
某个彩笔开发:这波有bug我吃shi。
然后测试小姐姐反手就给了我几个 bug
- bug1: 你这东西不准啊,我看着几分钟,有好几秒的延迟
- bug2: 你这东西有问题啊,我缩小浏览器,等一会再打开,延迟了几分钟
某个彩笔开发:不可能,我再自测一下,打你脸,我测的时候明明没问题。
五分钟后:额,额,额~emmmmmmmmm....
好嘛,我改。
原因
浏览器中的定时器任务是有误差的,也就是我们常说的 setTimeout 为什么不准的问题,这里涉及到 js 单线程以及运行机制,感兴趣的可以去了解一下,很多文章都有介绍。
在我的代码中,造成 bug 的原因:
- 没考虑误差的叠加,也就是没有处理误差
- 没考虑浏览器的"休眠"
处理
1. 没考虑误差的叠加,也就是没有处理误差
先看一下优化后的代码
let countdown = 100000; // ms 服务器返回的倒计时剩余时间
let countIndex = 1; // 倒计时任务执行次数
const timeout = 1000; // 触发倒计时任务的时间间隙
const startTime = new Date().getTime();
startCountdown(timeout);
function startCountdown(interval) {
setTimeout(() => {
const endTime = new Date().getTime();
// 偏差值
const deviation = endTime - (startTime + countIndex * timeout);
console.log(`${countIndex}: 偏差${deviation}ms`);
countIndex++;
// 下一次倒计时
startCountdown(timeout - deviation);
}, interval);
}
复制代码
几乎没有什么逻辑的执行快,就已经有每秒平均 5ms 的延迟,那么 10 分钟的延迟将会累加到 3000ms。
倒计时误差是不能避免的,但是我们能尽可能的减小这个误差。
// 下次倒计时任务执行的等待时间 = 1s - 误差 startCountdown(timeout - deviation); 复制代码
这里我们通过对下一次任务的调用时间做了调整, 前面延迟了多少毫秒,那么我下一个任务执行就加快多少毫秒 ,这就是处理倒计时误差的基本思路。
2. 没考虑浏览器的"休眠"
On most browsers inactive tabs have low priority execution and this can affect JavaScript timers.
用我蹩脚的英语翻译一下:在大多数浏览器中,待用的 tab 页优先级较低,这会对 JavaScript 的定时器造成影响。
上面的大多数浏览器不包括 ie,在 ie 下没有这个问题(ie真好用)。
蹩脚 + 概括:为了节能,对于运行在后台的 tab 页,定时器的延迟毫秒数被我们设置为 >= 1000ms。
相关链接:
- How can I make setInterval also work when a tab is inactive in Chrome?
- mozilla - Timeouts in inactive tabs throttled to >=1000ms
针对第二点
复制上面的代码,在浏览器调试器里面试试,切换 tab 页后,明显的可以看出延迟非常明显了。
关于这一点处理,其实更多的跟业务相关,不同的业务可能有不一样的处理方式。
由我的需求引申出一个例子:
- 网页实现一个 5 天的倒计时功能
- 倒计时的剩余数通过请求获取,初始为432000(s),也就是5天,并且服务器端也会进行一个倒计时
我猜,目前大部分的倒计时功能都是这样实现的,前端其实只是负责一个倒计时UI的显示,能让用户感知到有这么一回事,真正倒计时的还是放在了服务器端。
前面看到了切换 tab,或者网页最小化时,有延迟,那么我们只要 监听用户什么时候回到页面 ,这个时候再去请求服务器端最新的剩余时间,重新开始倒计时,修正造成的延迟。
目前有两种方案监听:
- window.focus + window.blur
- visibilitychange 事件
对于window.focus + window.blur,即使网页仍呈现在用户面前,也会触发此事件,比如从浏览器切到微信聊天(此时网页依然可见),但是仍会触发 window.blur 事件。
因此我利用 visibilitychange 事件来处理切换 tab 页以及浏览器最小化时的倒计时误差修正。
// 处理页面可见属性的改变
document.addEventListener('visibilityChange', () => {
if (!document.hidden) {
// get newest downtime
}
});
复制代码
总结
在查资料的同时,好像还有几种方案也可以实现倒计时。
但是 requestAnimationFrame 好像也不能解决第二种情况:
Web Worker 过两天玩一下,还没搞过呢。
第一次写文章,写得不好请见谅,也请指出。有更好的方法也可以分享一下,感恩~!
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Ant Colony Optimization
Marco Dorigo、Thomas Stützle / A Bradford Book / 2004-6-4 / USD 45.00
The complex social behaviors of ants have been much studied by science, and computer scientists are now finding that these behavior patterns can provide models for solving difficult combinatorial opti......一起来看看 《Ant Colony Optimization》 这本书的介绍吧!