为了账号安全,请及时绑定邮箱和手机立即绑定

利用setTimeout实现的倒计时功能出现跳一秒问题

利用setTimeout实现的倒计时功能出现跳一秒问题

jeck猫 2019-03-13 14:15:40
var period = 60 * 1000 * 60 * 2var endvar date = new Date(end)var interval = 1000var loop = function () {  if (!end) { end = new Date().getTime() + period }  var diff = end - new Date().getTime()  var h = Math.floor(diff / (60 * 1000 * 60))  var hdiff = diff % (60 * 1000 * 60)  var m = Math.floor(hdiff / (60 * 1000))  var mdiff = hdiff % (60 * 1000)  var s = Math.floor(mdiff / (1000))  console.log(h, m, s)  setTimeout(loop, interval)}setTimeout(loop, interval)上面是我写的一个倒计时方法,在控制台打印出的结果却总是跳了一秒,如下所示:
查看完整描述

5 回答

?
海绵宝宝撒

TA贡献1809条经验 获得超8个赞

解决了,第一次执行方法的时候就已经过了一秒了,所以在初始化end变量的时候就应该减去一秒!


var period = 60 * 1000 * 60 * 2

var end

var date = new Date(end)

var interval = 1000

function loop() {

  if (!end) { end = new Date().getTime() + period - interval / 1000 }

  var diff = end - new Date().getTime()

  var h = Math.floor(diff / (60 * 1000 * 60))

  var hdiff = diff % (60 * 1000 * 60)

  var m = Math.floor(hdiff / (60 * 1000))

  var mdiff = hdiff % (60 * 1000)

  var s = Math.floor(mdiff / (1000))

  console.log(h, m, s)

  setTimeout(loop, interval)

}

setTimeout(loop, interval)

上述代码会造成执行代码的时间累加问题,为了解决这个问题,参考这篇文章,更精确的倒计时应该是这样的:


var period = 60 * 1000 * 60 * 2

var startTime = new Date().getTime();

var count = 0

var end = new Date().getTime() + period

var interval = 1000

var currentInterval = interval


function loop() {

  count++

  var offset = new Date().getTime() - (startTime + count * interval); // 代码执行所消耗的时间

  var diff = end - new Date().getTime()

  var h = Math.floor(diff / (60 * 1000 * 60))

  var hdiff = diff % (60 * 1000 * 60)

  var m = Math.floor(hdiff / (60 * 1000))

  var mdiff = hdiff % (60 * 1000)

  var s = mdiff / (1000)

  var sCeil = Math.ceil(s)

  var sFloor = Math.floor(s)

  currentInterval = interval - offset // 得到下一次循环所消耗的时间

  console.log('时:'+h, '分:'+m, '毫秒:'+s, '秒向上取整:'+sCeil, '代码执行时间:'+offset, '下次循环间隔'+currentInterval) // 打印 时 分 秒 代码执行时间 下次循环间隔


  setTimeout(loop, currentInterval)

}


setTimeout(loop, currentInterval)

感谢各位不吝赐教。


查看完整回答
反对 回复 2019-03-18
?
达令说

TA贡献1821条经验 获得超6个赞

这个需求其实用setInterval更好,还不会出现你上述的问题:


var period = 60 * 1000 * 60 * 2

var end

var date = new Date(end)

var interval = 1000

var loop = function () {

  if (!end) { end = new Date().getTime() + period }

  var diff = end - new Date().getTime()

  var h = Math.floor(diff / (60 * 1000 * 60))

  var hdiff = diff % (60 * 1000 * 60)

  var m = Math.floor(hdiff / (60 * 1000))

  var mdiff = hdiff % (60 * 1000)

  var s = Math.floor(mdiff / (1000))

  console.log(h, m, s)

}

setInterval(loop, interval)


查看完整回答
反对 回复 2019-03-18
?
芜湖不芜

TA贡献1796条经验 获得超7个赞

https://img1.sycdn.imooc.com//5c8f080a0001345a08000644.jpgjs的所有异步时间都不是精确的。
观察一下可以看到每次都不是间隔1000毫秒,
一般的做法是储存下来diff数值,然后每次循环 从diff中减去period

查看完整回答
反对 回复 2019-03-18
?
拉风的咖菲猫

TA贡献1995条经验 获得超2个赞

可以参考我下面的代码,其中countDown函数接收倒计时的起始时间和结束时间(ms)。


其基本思路时倒推结束时间之前的所有计时时刻,setTimeout调用时,其时间延迟根据当前时刻与下一个计时时刻的时间差来设置,而不是一个固定值,这样就能避免累计误差。


参数可以比较灵活, 比如起始时间可以是函数调用的当前时间之前,如countDown(Date.now()-5000, Date.now() + 2 * 3600 * 1000),这样的话程序一开始会连续输出已经过去的所有计时时刻;如果起始时间在调用时间之后,那么会等到那个时间之后才开始倒计时,比如countDown(Date.now()+5000, Date.now() + 2 * 3600 * 1000)。



(function(){

    var now = Date.now();

    countDown(now, now + 2 * 3600 * 1000);


    function countDown(start, end){

        var interval = 1000,

            nextTick = start + ((end - start) % interval);

        var loopTime = function(){

            var now = Date.now();

            while(now >= nextTick){

                showTime(end - nextTick);

                nextTick += interval;

            }

            if(nextTick <= end){

                setTimeout(arguments.callee, nextTick - now);

            }

        };

        loopTime();

    }

    function showTime(ms){

        var sTotal = Math.floor(ms/1000),

            h = Math.floor(sTotal/3600),

            m = Math.floor(sTotal%3600/60),

            s = Math.floor(sTotal%60),

            timeStr = ('0' + h).slice(-2) + ':' +

                    ('0' + m).slice(-2) + ':' +

                    ('0' + s).slice(-2);

        console.log(timeStr);

    }

})();


查看完整回答
反对 回复 2019-03-18
?
开心每一天1111

TA贡献1836条经验 获得超13个赞

var period = 60 * 1000 * 60 * 2

var end

var date = new Date(end)

var interval = 100

var diff,h,hdiff,m,mdiff,s, res, res_tmp;

var loop = function () {

  if (!end) { end = new Date().getTime() + period }

  diff = end - new Date().getTime()

  h = Math.floor(diff / (60 * 1000 * 60))

  hdiff = diff % (60 * 1000 * 60)

  m = Math.floor(hdiff / (60 * 1000))

  mdiff = hdiff % (60 * 1000)

  s = Math.floor(mdiff / (1000))

  res_tmp = [h, m, s].join('-');

  if (!res || res != res_tmp) {

    res = res_tmp;

    console.log(res)

  }

  setTimeout(loop, interval)

}

setTimeout(loop, interval)

降低时间间隔。来排除setTimeout带来的偏差累计


查看完整回答
反对 回复 2019-03-18
  • 5 回答
  • 0 关注
  • 1747 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号