什么是针对不准确的setInterval的修复?

时间:2018-03-18 05:14:07

标签: javascript

请考虑以下代码:

var tick = 0;

setInterval(() => {
    console.log(tick);
}, 1000);

setInterval(() => {
  tick += 1;
}, 1);

基本上,我希望我的游戏看起来很流畅,所以我每1毫秒更新一次点的位置,这是第二个setInterval中的函数。

在1秒或1000毫秒内,我希望点移动1000像素。但是,它移动大约250像素。

当我运行以下jsfiddle时,我看到大约250的倍数被打印到控制台。

https://jsfiddle.net/58v74zw4/1/

根据我的观察,我可以通过乘以4来解决它,但感觉很烦人。

造成这种情况的原因是什么,以及对此有何正确解决方法?

1 个答案:

答案 0 :(得分:3)

我发现有关JS计时器的最佳文章:https://johnresig.com/blog/how-javascript-timers-work

  

由于JavaScript只能在异步事件发生时一次执行一段代码[...]它排队等待以后再执行[...]

这意味着如果有大量代码执行4ms,则间隔处理程序排队最多4ms。

 ms | event
----|--------------------------------------------
  0 | bigChunk() starts
  1 |
  2 | interval event fires
  3 |
  4 | bigChunk() returns
  5 | interval handler starts 
  

[...]浏览器[...]等待,直到排队更多时,不再有间隔处理程序排队等等。

这意味着如果有大量代码执行4ms,并且如果间隔设置为每1ms触发一次,则浏览器会在4ms后执行第一个间隔处理程序并删除3个事件。

 ms | event
----|--------------------------------------------
  0 | bigChunk() starts
  1 | interval event fires
  2 | interval event dropped
  3 | interval event dropped 
  4 | interval event dropped, bigChunk() returns
  5 | interval handler starts

在您的情况下,由于这个原因,可能会丢弃3/4(750/1000)的间隔事件,但也存在与实现相关的原因,例如此处描述的连续调用之间的最小延迟为4ms:https://stackoverflow.com/a/9647221/1636522 (信用:LGSon)。

正如您所看到的,setInterval在单个线程中使用时不可靠,但是,多线程并不一定会使事情变得更容易。例如,如果间隔处理程序的执行时间是4ms,则需要4个线程,每个线程之间的时移为1ms。

 Thread 1                      | Thread 2
-------------------------------|-------------------------------
 ms | event                    | ms | event
----|--------------------------|----|--------------------------
  0 | interval event fires     |  0 | 
  1 | interval handler starts  |  1 | interval event fires
  2 |                          |  2 | interval handler starts
  3 |                          |  3 |
  4 | interval handler returns |  4 |
  5 |                          |  5 | interval handler returns

这只是要解决的潜在问题之一。也就是说,如果你仍然想使用多线程,你可以看看Web Workers。我无法帮助,但我从未使用过这个功能: - | https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers

另请注意,“大多数浏览器都有60fps [每秒帧数]”(请参阅​​sunil),这意味着您有近17ms(1000/60)的时间来计算下一帧。因此,每毫秒更新一次游戏状态可能有点过头了: - )