Search code examples
javascriptloopssettimeoutframe-rate

Javascript setTimeout and frame rate


I seem to be having some unexpected results with a framerate counter in javascript. Up until recently the counter has been fine and I have been running my little js app at 30fps.

It uses setTimeout() (with a time adjustment to counter the system 'falling behind').

window.requestAnimFrame = (function() 
{
    return function (callback) {
        time += FPS;
        Heartbeat._eTime = (new Date().getTime() - Heartbeat._start);
        var diff = Heartbeat._eTime - time;

        Heartbeat._delta = FPS - diff;
        Heartbeat._deltaS = Heartbeat._delta / 1000;

        window.setTimeout(callback, FPS - diff);
    };
})();

Heartbeat is merely an object that contains the frame rate info.

*Here is my problem: *

_MainLoopHandler: function () {

    timer = new Date().getTime();
    counter = timer;

    while (this._messages.length > 0 && (counter - timer) < 5)
    {
        // process messages from _messages array
    }

    counter = new Date().getTime();
    // THE ABOVE IS HAPPY AT 30 FPS


    while ((counter - timer) < 6) {
        1 + 1;
    }
    // THE ABOVE WHILE IS VERY UNHAPPY :(

}

So the above code block is the function that is called from setTimeout every 33.33 milliseconds (30 fps). if I take the bottom while loop out, the FPS counter will sit happily at 30fps. However, if I leave it in, the FPS counter goes crazy. it goes up to the 200FPS 300FPS then suddenly goes -200FPS -10FPS 0.01FPS. Its completely off the wall. The while loop will only run maybe 10 times per "frame".

Note also, the hard-coded values 5 and 6 are simply a check to see if 5 or 6 milliseconds have passed while processing the loops (for load balance).

Is this simply javascript being unable to handle the amount of info or has anyone else had a similar problem.

Thanks!


Solution

  • I don't really know what's going on, but I think you should use local variables to control your time, constantly reassess counter and process 1 message at a time. Also, I don't really understand that last loop (I've also renamed the variables):

    _MainLoopHandler: function () {
    
      var start = new Date().getTime();
      var current;
    
      do {
        if (this._messages.length === 0) break;
        // process 1 message
        current = new Date().getTime();
      } while (current - start < 5);
    
    }
    

    You can also encapsulate the timing concern in an object (not shown) to streamline the code:

    _MainLoopHandler: function () {
    
      var timing = new Timing();
    
      do {
        if (this._messages.length === 0) break;
        // process 1 message
      } while (timing.elapsed() < 5);
    
    }