【问题标题】:Creating an Accurate Timer in Javascript, but I sometimes get a negative Interval Time在 Javascript 中创建一个准确的计时器,但我有时会得到一个负的间隔时间
【发布时间】:2017-11-19 13:51:14
【问题描述】:

我不能使用很多 NPM 库来获得准确的计时器,因为它依赖于我没有使用的 Node.. 但公式非常简单。这是我正在使用的 JSfiddle:

http://jsfiddle.net/zryNf/9/

setPercentageBar(page) {
    if (this.state.pagesById[page.id].active) {
      let pagesByIdCopy = Object.assign({}, this.state.pagesById);
      let pageCopy = pagesByIdCopy[page.id];

      let percent = (100.0 / parseFloat(page.durationTime)) / 10;
      pageCopy.percentage += percent;
      this.setState({pagesById: pagesByIdCopy});

      pageCopy.progressBarNextAt += this.progressBarTime;  // this.progressBarTime = 100

      console.log('start time');
      console.log(pageCopy.progressBarStart);
      console.log('next at');
      console.log(pageCopy.progressBarNextAt);
      console.log('drift');
      console.log((new Date().getTime() - pageCopy.progressBarStart) % 100);
      console.log('Date().getTime():');
      let currTime = new Date().getTime();
      console.log(currTime);
      console.log('interval time');
      console.log(pageCopy.progressBarNextAt - currTime);
      console.log('--------------------------------------------------------\n');

      if (pageCopy.percentage < 100) {
        pageCopy.progressBarTimer = setTimeout(() => this.setPercentageBar(pageCopy),
          pageCopy.progressBarNextAt - currTime);
      }
    }
  }

这不是我在 React 中所做的确切代码,但它是完全相同的公式。

除了使用 100 毫秒延迟外,我正在使用它来更新进度条,并且我需要进度条顺利展开。

我正在记录 startTime、nextAt 时间、漂移和间隔时间,而且开始时总是很好。但是在某些时候..间隔时间变成负数,我不知道为什么!然后从那一刻起,一切都开始变得混乱。我已经尝试了这个精确计时器的多个公式,最终每次都会发生。一旦间隔时间变为负数..它们都变为负数。

任何人都知道为什么以及我可以做些什么来解决它?

这里有一些示例日志

https://pastebin.com/Cpfpf1Gp

编辑:我最多有 5 个计时器,每个计时器运行 100 毫秒

【问题讨论】:

  • 计时器从不精确...而且新日期也不是...johnresig.com/blog/accuracy-of-javascript-time
  • 在你的小提琴中你写了“这个作品!”,那么问题是什么?
  • @trincot 查看我在 pastebin 上发布的日志.. 你可以看到间隔在某些时候变成了负数,我试图弄清楚它为什么会这样做,以便我可以解决这个问题。
  • 如果您只提供问题的日志以及显然不会产生问题且不是您的代码的代码,您希望我们如何分析问题?你必须提供一些我们可以复制它的东西。
  • 你们这些投反对票的人对这个社区来说太可怕了。人们只是想在这里寻求帮助。如果我们没有正确提出要求,但我们真的想提供所有我们能提供的信息,为什么要投反对票。

标签: javascript jquery reactjs timer ecmascript-6


【解决方案1】:

无法保证setTimeout 回调函数将在所需时间准确执行。如果此时其他 JavaScript 代码正在运行,则必须首先运行完成,然后才能处理事件队列中的下一个事件,而计时器事件就是这样的事件。

所以很可能你会得到以下场景:

  • 0ms 时:currTime 为 0,progressBarNextAt 为 100,您调用 setTimeout(progressBarNextAt, 100)。一切都很完美。
  • 在 1 毫秒:事件队列中的下一个事件是正在发生的实际处理的一些回调
  • 在 220 毫秒:回调结束,事件队列中的下一个事件是计时器(迟到),您的函数 setPercentageBar 被第二次调用
  • 在 221 毫秒:progressBarNextAt 从 100 增加到 200,这还不够,因为我们已经过了那个时刻。您打印的间隔时间为 -21。

甚至可以在定时器事件被处理之前安排一些异步代码,以微任务的形式。例如,如果第二个要点中的处理有一些立即解决的承诺,则相应的then 回调函数仍将作为该任务的一部分执行,并且只有当所有这些都已执行时,才会轮到计时器事件.

还要注意,非 JS 因素会影响某个任务需要多长时间。如果机器负载过重,并且操作系统重新分配资源,这可能会影响原本较轻的 JavaScript 任务。当有问题的窗口不在前台时,浏览器也倾向于延迟计时器事件的处理。还有其他原因。

【讨论】:

    【解决方案2】:

    问题中的代码似乎采用了一些非常简单的方法,使它有点复杂。与其尝试通过“漂移”计算来弥补这种复杂性,我们为什么不采用更简单的方法呢?

    关键是要把这两件事解耦:

    1. 何时更新进度条。
    2. 如何计算当前的完成百分比或百分比。

    相反,我们只需要一个update() 函数,它可以随时调用,并且总是做正确的事。它的计算完全不取决于我们调用它的频率或时间。

    另外,当您使用setTimeout()setInterval() 制作动画时,您可能会得到一些相当跳跃的结果。我们可以使用requestAnimationFrame() 来代替更平滑的动画。 (它仍然不完美,但比你用计时器得到的要好。)

    所以它可能看起来像这样:

    function progressBar( duration ) {
        setValue( 0 );
        var startTime = +new Date;
        update();
        function update() {
            var elapsed = +new Date - startTime;
            if( elapsed > duration ) elapsed = duration;
            if( elapsed < duration ) requestAnimationFrame( update );
            setValue( elapsed / duration );
        }
        function setValue( fraction ) {
            var percent = Math.floor( fraction * 100 );
            document.getElementById('percent').innerHTML =
                percent + '%';
            document.getElementById('filler').style.width =
                fraction * 100 + '%';
        }
    }
    
    progressBar( 5000 );
    #border {
        border-color: black;
        border-width: 1px;
        width: 100%;
        height: 20px;
    }
    
    #filler {
        background-color: green;
        width: 0;
        height: 100%;
    }
    <div id="border">
      <div id="filler">
          &nbsp;
      </div>
    </div>
    <div id="percent"><div>

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-07-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多