javascript倒计时代码 攻略教程JS实现活动精确倒计时思路和方法_活动使用帮助

背景前端页面倒计时功能在很多场景中会用到,如运营活动开始倒计时和活动结束倒计时,又如购物网站的秒杀倒计时,抢购倒计时,还有我们手Q春节抢红包倒计时等等……. 最近的话费代付项目中,也涉及倒计时功能,但在开发过程中遇到一些麻烦和坑点,下面和大家分享一下最后是如何解决的。坑点手Q春节抢明星红包活动,就有产品吐槽两个手机在不同时间点打开同一个活动显示的开抢倒计时不

javascript倒计时代码 攻略教程JS实现活动精确倒计时思路和方法

背景

前端页面倒计时功能在很多场景中会用到,如运营活动开始倒计时和活动结束倒计时,又如购物网站的秒杀倒计时,抢购倒计时,还有我们手Q春节抢红包倒计时等等……. 最近的话费代付项目中,也涉及倒计时功能,但在开发过程中遇到一些麻烦和坑点,下面和大家分享一下最后是如何解决的。

javascript倒计时代码 攻略教程JS实现活动精确倒计时思路和方法_活动使用帮助

坑点

手Q春节抢明星红包活动,就有产品吐槽两个手机在不同时间点打开同一个活动显示的开抢倒计时不一样,误差大的甚至相差几分钟,导致某些用户在活动显示还未开始红包就已被抢完了。为什么误差会这么大呢?

对于这个问题,前台开发同学一般会猜测是这个原因:倒计时读取了客户端时间造成的,因为客户端时间和服务端时间有误差,应该读取服务器时间。

是的,倒计时不应该读取客户端时间,客户端时间用户可以随时调整,会造成不一致,应读取服务器返回时间。但实践证明,做了这一步还未够,页面运行时间长了,新开的页面和原打开页面还是存在误差。

京东团购也存在这个问题:

javascript倒计时代码 攻略教程JS实现活动精确倒计时思路和方法_活动使用帮助

造成误差的原因主要有几种可能:

1. 没有考虑js冻结运行耗费时间;(特别是移动端容易出现,下滑页面时倒计时不动了)

2. 没有考虑页面渲染和函数运行累积时间;(京东的误差貌似属于这种)

3. 其他代码逻辑问题(这种情况就复杂了,这里不讨论);

计时器原理

倒计时功能离不开setTimeout或setInterval这两个函数,要用好这两个函数必先了解好Javascript解释器的工作原理,前端界大牛John.Resig (jQuery作者) 有篇文章很好讲解了Javascript解释器工作原理 — 《How JavaScript Timers Work》。

javascript倒计时代码 攻略教程JS实现活动精确倒计时思路和方法_活动使用帮助

这图包含信息量很大,这里按照自己的理解描述一下:

这图从上往下看,垂直方向是时间,以ms为单位,蓝色模块是执行代码所占的时间段,如第一个代码模块执行js占用了约18ms, 第二个模块执行js占用了约11ms,其他模块类似。由于js是单线程执行,同一时间只能执行一个js代码(同一时间其他异步事件执行会被阻塞 ) , 当异步事件发生时,它会进入代码执行队列,执行线程空闲时依照队列顺序依次执行代码。

第一个模块初始化了两个定时器,一个10ms延迟的setTimeout和10ms的setInterval。这些定时器可能会在我们第一个代码块执行结束之前就触发,这取决于定时器在第一个代码块中启动的位置和时间。注意,定时器虽然触发了,但是并不会立即执行,它只是把需要延迟执行的函数按时间先后加入了执行队列,在线程的某一个空闲的时间点,这个函数就能够得到执行。

按照第一个模块事件触发的顺序(Mouse Click Occurs -. 10ms Timer Fires),第一个模块代码执行结束后,按照队列中等待的先后顺序执行事件,先执行Mouse Click CallBack再执行Timer。在执行Mouse Click CallBack模块时,Interval第一次触发未执行加入队列。在执行Timer模块时,Interval第二次触发未执行加入队列。待Mouse Click CallBack和Timer模块都执行完毕后,再依次执行队列中已触发的Interval事件。后面模块由于没有阻塞的事件了,所以按照既定10ms执行Interval事件。

倒计时问题

如果上面Javascript计时器原理理解了,就很好明白倒计时功能存在问题的隐患。

先看一段测试代码:

var start = new Date().getTime();
var count = 0;
 
//定时器测试
setInterval(function(){
     count++;
     console.log( new Date().getTime() - (start + count * 1000)); 
},1000);

目测代码就知道运行结果,定时器每秒执行一次,每次输出应该是0 。

实际输出:

javascript倒计时代码 攻略教程JS实现活动精确倒计时思路和方法_活动使用帮助

结论:由于代码执行占用时间和其他事件阻塞原因,导致有些事件执行延迟了几ms,但影响很微。

下面加一段阻塞代码看看:

var start = new Date().getTime();
var count = 0;
 
//占用线程事件
setInterval(function(){
     var j = 0;
     while(j++ < 100000000);
}, 0);
 
//定时器测试
setInterval(function(){
     count++;
     console.log( new Date().getTime() - (start + count * 1000)); 
},1000);

实际输出:

javascript倒计时代码 攻略教程JS实现活动精确倒计时思路和方法_活动使用帮助

结论:由于加了很占线程的阻塞事件,导致定时器事件每次执行延迟越来越严重。

由于实际项目中,执行计时器的同时,会有很多其他异步阻塞事件,会导致倒计时功能不精确。

解决思路

这里先分析一下从获取服务器时间到前端显示倒计时的过程:

1. 客户端http请求服务器时间;

2. 服务器响应完成;

3. 服务器通过网络传输时间数据到客户端;

4. 客户端根据活动开始时间和服务器时间差做倒计时显示;

javascript倒计时代码 攻略教程JS实现活动精确倒计时思路和方法_活动使用帮助

服务器响应完成的时间其实就是服务器时间,但经过网络传输这一步,就会产生误差了,误差大小视网络环境而异,这部分时间前端也没有什么好办法计算出来,一般是几十ms以内,大的可能有几百ms。

可以得出:当前服务器时间 = 服务器系统返回时间 + 网络传输时间 + 前端渲染时间 + 常量(可选),这里重点是说要考虑前端渲染的时间,避免不同浏览器渲染快慢差异造成明显的时间不同步,这是第一点。(网络传输时间忽略或加个常量呗)

获得服务器时间后,前端进入倒计时计算和计时器显示,这步就要考虑js代码冻结和线程阻塞造成计时器延时问题了,我的思路是通过引入计数器,判断计时器延迟执行的时间来调整,尽量让误差缩小,不同浏览器不同时间段打开页面倒计时误差可控制在1s以内。

关键实现代码如下:

//继续线程占用
setInterval(function(){
     var j = 0;
     while(j++ < 100000000);
}, 0);
 
//倒计时
var  interval = 1000,
       ms = 50000,  //从服务器和活动开始时间计算出的时间差,这里测试用50000ms
       count = 0,
       startTime = new Date().getTime();
if( ms >= 0){
       var timeCounter = setTimeout(countDownStart,interval);                  
}
 
function countDownStart(){
       count++;
       var offset = new Date().getTime() - (startTime + count * interval);
       var nextTime = interval - offset;
       var daytohour = 0;
       if (nextTime < 0) { nextTime = 0 };
       ms -= interval;
       console.log("误差:" + offset + "ms,下一次执行:" + nextTime + "ms后,离活动开始还有:" + ms + "ms");
       if(ms < 0){
              clearTimeout(timeCounter);
       }else{
              timeCounter = setTimeout(countDownStart,nextTime);
       }
}

运行结果:

javascript倒计时代码 攻略教程JS实现活动精确倒计时思路和方法_活动使用帮助

结论:由于线程阻塞延迟问题,做了setTimeout执行时间的误差修正,保证setTimeout执行时间一致。若冻结时间特别长的,还要做特殊处理。

倒计时组件

组件:http://imgcache.gtimg.cn/club/common/lib/zero/widgets/date/Date.1.1.1.js

文档:http://docs.oa.com/p/zero_widgets#qv.zero.Date.countDown

javascript倒计时代码 攻略教程JS实现活动精确倒计时思路和方法_活动使用帮助

应用项目地址:http://m.vip.qq.com/clubact/2014/jdfl/index.html?_wv=1

总结

做100%精确的倒计时很难,但做到相对比较准确是可以的。

在倒计时功能开发中,有几点总结:

1. 要了解好js单线程工作原理;

2. 清楚了解服务器系统时间传送到前端的流程;

3. 了解前端渲染和线程阻塞造成的时间误差;

希望本文对需要用到倒计时功能的童鞋有所帮助,后续会针对一些大型网站使用的倒计时功能对比分析,实现更精确的倒计时功能。

本文来源:JS实现活动精确倒计时 – 轩枫阁

海计划公众号
(0)
上一篇 2020/04/05 02:00
下一篇 2020/04/05 02:00

您可能感兴趣的内容

  • 哪种编程语言最适合区块链?小白指南_语言小白入门

    区块链技术由于其安全、快速以及去中心化的特性(虽然不是所有项目都满足这些特性,但是大多数项目是符合的)已经迅速渗透到了各种业务场景、我们的生活以及身边的企业中。数以千计的区块链项目正在开发,或者已经开发完成并且部署到了网络中。各个企业都在寻求区块链的新点子或者试图去改进其他公司正疯狂创造的东西,然而这仅仅是区块链技术驱动这个世界的开始。作为开发者来说,在区块

    2020/03/23
  • MySQL时间类型和模式小白帮助_模式基础入门

    当我在MySQL数据库中尝试插入一条带有时间戳的数据时报错:mysql> insert into alarm_service values (6, ‘1970-01-01 08:00:00’);
    ERROR 1292 (22007): Incorrect datetime value: ‘1970-01-01 08:00:00’ for column ‘t

    2020/03/30
  • Node中异常:exit code与 dockerfile菜鸟知识_异常菜鸟指南

    最近观察项目 CI 跑的情况如何时,会偶尔发现一两个镜像虽然构建成功但是容器跑不起来的情况。究其原因,是因为一个 exit code 的问题throw new Error 与 Promise.reject 区别以下是两段代码,第一个是抛出一个异常,第二个是 Promise.reject,两段代码都会如下打印出一段异常信息,那么两者有什么区别?function

    2020/03/26
  • 关于SbWebServer页面缓存的设计攻略教程_缓存指南教程

    对于一个网站页面来说,不同的页面被访问的可能性不同,像主页被访问的概率是最大的。如果利用这个特点,对高访问概率的页面存入缓存,这样每次连接过来就不用每次都要经历本地找文件,打开这样一个过程。对于这个缓存的设计,首先考虑:1.主页一定是一直在缓存中的。2.用一个哈希表来建立filename—>文件在内存中地址的映射。3.用shared_ptr来指向文件地址

    2020/03/29
  • easy-pie-chart菜鸟教程_非常轻量级、简单、具有动画效果的饼图组件

    easy-pie-chart菜鸟教程 官方网址:http://rendro.github.io/easy-pie-chart GitHub:https://github.com/r…

    2020/03/06
  • 小程序实现瀑布流和上拉加载入门知识_瀑布流使用教程

    小程序实现瀑布流和上拉加载入门知识 瀑布流又称瀑布流式布局,是比较流行的一种网站页面布局方式。视觉表现为参差不齐的多栏布局,即多行等宽元素排列,后面的元素依次添加到其后,等宽不等高…

    2020/03/19
  • 一篇文章教会你面试中的小套路基础教程_面试小白教程

    面试,一个短时间内对某个人做出判断的测试。很多时候,工程师们只准备了相关的技术点,却没有对非技术的软实力部分做准备。而软实力的考察,不仅贯穿整个面试流程中,更在BOSS面和HR面中尤为关键。鉴于当前业界也没有特别契合的攻略文档,仅有有几篇文章还是HR写的,特有此文。在本文中,我们将从环境与心态,相关问题和表达技巧三个方面阐述如何准备面试,以确保在硬实力的OK

    2020/04/03
  • 前端布局方式汇总及概念浅析入门攻略_布局入门基础教程

    一、基础布局方式0. 普通/文档流 布局早期

    , 后来 ,再后来 HTML5 语意化标签按照自上而下的方式顺次排布。1. Float 布局 float: left/right最初设计目的是用于图文环绕排版、不过目前常用于左右布局。2. 绝对布局 position: absolute,position: fixed保持与目标元素(po

    2020/04/05
  • manypixels小白知识_免版权小人场景矢量插画素材

    manypixels小白知识 官方网址:https://gallery.manypixels.co/ 简介描述:免版权小人场景矢量插画素材 一个可编辑,免费的SVG插图.&nbsp…

    2020/03/10
  • forward和redirect的区别?http状态码301,302分别代表什么?新手入门_小白教程

    一、forward和redirect的区别从地址栏显示来说:forward是服务器内部重定向,客户端浏览器的网址不会发生变化;redirect发生一个状态码,告诉服务器去重新请求那个网址,显示的的新的网址数据共享:forward使用的是同一个request,可以共享request的数据,redirest不可以应用场景:forward一般用于用户登录:redi

    2020/03/23
  • 10个常用的CSS动画库入门基础_动画菜鸟知识

    1.Animista网站地址:http://animista.net/网站描述:在线生成 css 动画Animista是一个在线动画生成器,同时也是一个动画库,它为我们提供了以下功能1. 选择不同的动画我们可以选择想要的动画类型(例如entrance/exist),除了可以选择某个动画(例如,scale-in)外,甚至还可以为该动画选择不同的展示效果(例如:

    2020/03/20
  • Node.JS中回调嵌套和async/await执行空函数性能效率对比测试使用帮助_测试小白指南

    Node.JS中回调嵌套和async/await执行空函数性能效率对比测试使用帮助 asyn/await关键字可以让原来的回调嵌套和链式写法,改造成同步语法。util.promis…

    2020/03/20
  • 如何解决在vue中this报错undefined入门教程_undefined零基础入门

    当你开心地在编程,惊叹于vue的神奇,这时你却遇到这样的情况:你的vue应用无法正常工作,你收到的报错是:this is undefined产生问题的原因是你混合使用了普通函数和箭头函数。我猜你肯定用了一个箭头函数。如果你把这个箭头函数替换成普通函数,也许能解决上面你遇到的问题。接下来让我们深入原理来了解为什么会产生这个问题。毕竟,知识是强大的,如果你知道了

    2020/04/03
  • “回调函数”超难面试题!!入门百科_函数使用帮助

    let app = {ary: [],use(fn) {this.ary.push(fn);}};app.use((next) => {console.log(1);next();console.log(2)});app.use((next) => {console.log(3);next();console.log(4)});app.use

    2020/04/03
  • DPlayer简单用法基础教程_视频使用教程

    DPlayer 是一个支持弹幕的 HTML5 视频播放器。支持 Bilibili 视频和 danmaku,实时视频(HTTP Live Streaming,M3U8格式)以及 FLV 格式。 用法HTML


    <script src="dist/DPlaye

    2020/03/20
  • 利用Hexo来快速搭建博客系统菜鸟教程网_Hexo入门基础知识

    Hexo是什么Hexo 是一个快速、简洁且高效的博客框架。Hexo 使用 Markdown(或其他渲染引擎)解析文章,在几秒内,即可利用靓丽的主题生成静态网页。Hexo安装要使用Hexo,需要在你的系统中支持Nodejs以及Git。 Node.js下载:https://nodejs.org/en/Git下载:http://git-scm.com/downl

    2020/04/05