javascript中的超时与setTimeout不一致?

javascript中的超时与setTimeout不一致?,javascript,html,canvas,Javascript,Html,Canvas,我在做游戏。游戏有1个主循环: //draws a new frame and game logic function draw() { player.gameTick(); game.gameTick(); lastTime=newTime; background.draw(ctx); player.draw(ctx); enemies.draw(ctx); setTimeout(draw,50); } 正常情况下,这运行良好,我得到20 fp

我在做游戏。游戏有1个主循环:

//draws a new frame and game logic
function draw()
{
   player.gameTick();
   game.gameTick();

   lastTime=newTime; 

   background.draw(ctx);
   player.draw(ctx);
   enemies.draw(ctx);
   setTimeout(draw,50);
}
正常情况下,这运行良好,我得到20 fps报告给#控制台。然而,偶尔fps峰值会超过125。(表示在上次调用后不到50毫秒调用draw)。当这种情况发生时,游戏开始延迟几秒钟,然后fps返回。(这也是违反直觉的,为什么较高的fps会导致滞后?)

不管怎样,有人知道为什么会这样吗


是的,我也尝试过setInterval(),同样的情况也发生了=/

JavaScript是单线程的。如果您单独安排两次50毫秒的超时,则它们有可能最终发生碰撞或接近碰撞,并导致一秒钟的奇怪处理阻塞,然后才能自行清理并再次腾出空间。您可能应该合并代码并创建一个调用其他两个函数的主循环。这样,您可以确保它们每50毫秒处理一次

您的流程如下所示:

function mainLoop() {
  player.tick();
  game.tick();
  background.draw();
  players.draw();
  enemies.draw();
  setTimeout(mainLoop, 50);
}

mainLoop()
  • Process1()->需要一些时间,希望不到50毫秒,但保证>0毫秒
  • Process1在将来为自己设置一个50毫秒的超时。Process1的第二次运行将在第一次启动后超过50毫秒
  • Process2()->需要一些时间,大于0毫秒。与Process1相同
  • Process2为自身设置超时。同样的规则也适用。Process2的第二次运行将在第一次启动后超过50毫秒
  • 如果从理论上讲,Process1需要5毫秒,Process2需要7毫秒,则Process1或Process2每运行7次,将导致未来设置为50毫秒的下一个超时与另一个函数下一次运行的计划时间完全对应。当一次只能做一件事的解释器被要求同时处理多个事件时,这种类型的冲突将导致不一致的行为

    --编辑您对问题的修订--

    您仍将在将来设置两个50毫秒的独立超时。仍然没有办法防止这些碰撞,我仍然不能完全确定你为什么要这样接近它。你应该有这样的东西:

    function mainLoop() {
      player.tick();
      game.tick();
      background.draw();
      players.draw();
      enemies.draw();
      setTimeout(mainLoop, 50);
    }
    
    mainLoop()
    

    请注意,没有重复的setTimeout调用。一切都可以一下子发生。您正在演示的代码的两个版本中创建冲突。

    此问题是由浏览器javascript计时器节流引起的。我注意到,当我打开许多选项卡或在选项卡之间切换时,问题变得更严重。Chrome没有那么大的问题,很可能是因为标签处于独立的进程中


    Firefox7似乎已经为FF解决了这个问题

    所以首先,你知道你不能保证两个请求之间有50毫秒的间隔,对吗?好吧,跳过我说的要阻止更多评论的那一点,在125+fps之前会发生什么?另外,您将
    lastTime
    设置在何处?有点离题,但如果您实际在
    draw
    函数中运行
    $(“#console”)
    ,请将其缓存在函数外部。每秒执行20次相同的DOM选择是很不幸的。@patrickdw,是的,但是在没有执行DOM选择和写入的情况下,显示了相同的行为=/在jcolebrand的评论之后,您会想知道setTimeout中指定的函数将在50毫秒后添加到调用堆栈中。它只会在堆栈最终到达该函数时执行。见Jon Resig关于这个主题的文章:这实际上是我最初的做法。我有一个处理重画和逻辑的循环。同样的问题也发生了。对,我理解碰撞的问题。我将把两个循环折叠成一个。但问题是,我本来就是这样做的。当这两个是1循环时,发生了完全相同的问题。是什么原因造成的?@razorstrom-我还不能100%保证这一点。我需要更多地了解您的代码以及它是如何设置的。正如jcolebrand所问的,fps“spike”之前会发生什么?><这是一个编辑错误。这里应该只有一个setTimeout调用,哈哈哈。至于在经济放缓之前发生了什么,这似乎没什么。这几乎是随机发生的。然而,在切换到另一个选项卡并返回后(在firefox6中),这种情况会发生得更多。所以我假设这与浏览器节流有关。