JavaScript 倒计时踩坑集锦

栏目: JavaScript · 发布时间: 6年前

内容简介:前阵子,项目中加了个倒计时的需求,接手的时候啪啪啪三声,搞定,送测某个彩笔开发:这波有bug我吃shi。

前阵子,项目中加了个倒计时的需求,接手的时候

JavaScript 倒计时踩坑集锦

啪啪啪三声,搞定,送测

let countdown = 100000; // ms 服务器返回的倒计时剩余时间

function startCountdown() {
    setTimeout({
        countdown -= 1000;
        if (/* some */) {
            // do some
            startCountdown();
        }
    }, 1000);
}
复制代码

某个彩笔开发:这波有bug我吃shi。

然后测试小姐姐反手就给了我几个 bug

JavaScript 倒计时踩坑集锦
  • 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。

JavaScript 倒计时踩坑集锦

倒计时误差是不能避免的,但是我们能尽可能的减小这个误差。

// 下次倒计时任务执行的等待时间 = 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真好用)。

JavaScript 倒计时踩坑集锦

蹩脚 + 概括:为了节能,对于运行在后台的 tab 页,定时器的延迟毫秒数被我们设置为 >= 1000ms。

相关链接:

针对第二点

复制上面的代码,在浏览器调试器里面试试,切换 tab 页后,明显的可以看出延迟非常明显了。

JavaScript 倒计时踩坑集锦

关于这一点处理,其实更多的跟业务相关,不同的业务可能有不一样的处理方式。

由我的需求引申出一个例子:

  • 网页实现一个 5 天的倒计时功能
  • 倒计时的剩余数通过请求获取,初始为432000(s),也就是5天,并且服务器端也会进行一个倒计时

我猜,目前大部分的倒计时功能都是这样实现的,前端其实只是负责一个倒计时UI的显示,能让用户感知到有这么一回事,真正倒计时的还是放在了服务器端。

前面看到了切换 tab,或者网页最小化时,有延迟,那么我们只要 监听用户什么时候回到页面 ,这个时候再去请求服务器端最新的剩余时间,重新开始倒计时,修正造成的延迟。

目前有两种方案监听:

对于window.focus + window.blur,即使网页仍呈现在用户面前,也会触发此事件,比如从浏览器切到微信聊天(此时网页依然可见),但是仍会触发 window.blur 事件。

因此我利用 visibilitychange 事件来处理切换 tab 页以及浏览器最小化时的倒计时误差修正。

// 处理页面可见属性的改变
document.addEventListener('visibilityChange', () => {
    if (!document.hidden) {
      // get newest downtime
    }
});
复制代码

总结

在查资料的同时,好像还有几种方案也可以实现倒计时。

但是 requestAnimationFrame 好像也不能解决第二种情况:

JavaScript 倒计时踩坑集锦

Web Worker 过两天玩一下,还没搞过呢。

第一次写文章,写得不好请见谅,也请指出。有更好的方法也可以分享一下,感恩~!


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

The Linux Command Line

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》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

SHA 加密
SHA 加密

SHA 加密工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具