Inconsistent timeouts in javascript with setTimeout?
I am making a game. The game has 1 main loops:
//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);
}
Normally this operates fine, and I get 20fps reported to #console. Once in a while, however, the fps spikes up to >125. (meaning draw is being called less than 50 mill开发者_C百科iseconds after the previous call). When this happens, the game starts to lag for a couple of seconds, and then the fps goes back down. (that's also counter intuitive, why does HIGHER fps cause lag?)
Anyway, does anyone know why this is the case?
Yes I have tried setInterval() also, and the same thing happens. =/
JavaScript is single threaded. If you're scheduling two timeouts for 50 ms independently, there's a chance that they eventually collide, or come close to it, and cause an odd processing blockage for a second before they sort themselves out and can space out again. You should probably consolidate the code and create a single main loop that calls the other two functions. That way you can ensure that they both process once each per 50 ms.
Your flow looks something like this:
- Process1() -> takes some amount of time, hopefully less than 50ms, but guaranteed to be > 0ms.
- Process1 sets a timeout for itself 50ms in the future. The 2nd run of Process1 will occur more than 50ms after it started the 1st time.
- Process2() -> takes some amount of time, greater than 0ms. Same as Process1.
- Process2 sets a timeout for itself. Same rules apply. The 2nd run of Process2 will occur more than 50ms after it started the 1st time.
If, theoretically, Process1 takes 5ms and Process2 takes 7ms, every 7 runs of Process1 or 5 runs Process2 will cause the next timeout set for 50ms in the future to correspond exactly with the scheduled time for the next run of the other function. This type of collision will cause inconsistent behavior when the interpreter, which can only do one thing at a time, is asked to handle multiple events concurrently.
-- Edits for your revisions to the question --
You're still setting two independent timeouts for 50ms in the future. There's still no way to prevent these from colliding, and I'm still not entirely certain why you're approaching it this way. You should have something like this:
function mainLoop() {
player.tick();
game.tick();
background.draw();
players.draw();
enemies.draw();
setTimeout(mainLoop, 50);
}
mainLoop()
Note the absence of repetitive setTimeout calls. Everything can happen in one go. You're creating collisions in both versions of your demonstrated code.
This problem was caused by browser javascript timer throttling. I noticed the problem was exacerbated when I have many tabs open or when I switch between tabs. Chrome had less of an issue, most likely due to tabs being in isolated processes.
Firefox 7 seems to have fixed this problem for FF.
精彩评论