JavaScript 倒计时踩坑集锦
栏目: JavaScript · 发布时间: 6年前
内容简介:前阵子,项目中加了个倒计时的需求,接手的时候啪啪啪三声,搞定,送测某个彩笔开发:这波有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 过两天玩一下,还没搞过呢。
第一次写文章,写得不好请见谅,也请指出。有更好的方法也可以分享一下,感恩~!
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
The Linux Command Line
William E. Shotts Jr. / No Starch Press, Incorporated / 2012-1-17 / USD 39.95
You've experienced the shiny, point-and-click surface of your Linux computer-now dive below and explore its depths with the power of the command line. The Linux Command Line takes you from your very ......一起来看看 《The Linux Command Line》 这本书的介绍吧!