0

I need a very fast timer in Javascript. The timer resolution is considered to be 10 milliseconds. I already implemented a working timer, but the problem is, that the time is always depending on how many other ressources are running in the Browser. And when an animated GIF is running, my timer won't work at all. This is the code (working!):

var timer = new Timer();

function Timer(){
    var time = 0;
    //Every 10ms we are incrementing the timer:
    setInterval(function(){
        time=time+1;
    },10);
    this.getTime=function(){return time;}
}

function testTimer(){
    var counter = 0;
    var resultsum = 0;
    var everytensum = 0;
    setInterval(function(){
        counter = counter + 1;
        window.timeoutTesterRunning = true;
        var timeoutTester = new TimeoutTester();
        setTimeout(function(){
            console.log(counter+": "+timeoutTester.getResult());
            resultsum = resultsum+timeoutTester.getResult();
            console.log(resultsum);
            if(counter%10==0){
                console.log("Counting last 10 seconds: "+(resultsum-everytensum));
                console.log("Counting last "+counter+" seconds: "+resultsum);
                everytensum = resultsum;
            }
        },1200)
    }, 3000);
}

function TimeoutTester(){
    var result;
    var snap_time1 = timer.getTime();
    setTimeout(function(){
        var snap_time2 = timer.getTime();
        result = snap_time2-snap_time1;
    },1000);
    this.getResult=function(){return result;}
}

Now the results from the console.log (the sums are in brackets):

[10:10:54.466] 1: 100 (100)
[10:10:57.466] 2: 100 (200)
[10:11:00.466] 3: 100 (300)
[10:11:03.466] 4: 100 (400)
[10:11:06.466] 5: 100 (500)
[10:11:09.465] 6: 100 (600)
[10:11:12.466] 7: 101 (701)
[10:11:15.467] 8: 100 (801)
[10:11:18.467] 9: 100 (901)
[10:11:21.467] 10: 100 (1001)
[10:11:21.476] Counting last 10 seconds: 1001
[10:11:24.467] 11: 100 (1101)
[10:11:27.468] 12: 100 (1201)
[10:11:30.467] 13: 100 (1301)
[10:11:33.478] 14: 60 (1361)  //Animated Gif started: Less countings! ("Time runs longer")
[10:11:36.476] 15: 57 (1418)
[10:11:39.482] 16: 58 (1476)
[10:11:42.472] 17: 61 (1537)
[10:11:45.474] 18: 56 (1593)
[10:11:48.484] 19: 48 (1641)
[10:11:51.470] 20: 55 (1696)
[10:11:51.476] Counting last 10 seconds: 695
[10:11:51.482] Counting last 20 seconds: 1696

The counter runs quite regular. But when the Browser is busy (by a animated GIF), Javascript has no time to set the intervals correctly. The function Timer() just doesn't receive the time it needs to increment var time, so the values are far to low (when I first started testing, the values were even lower at ~40, but I was able to rise them to ~55 by using functional implementation).

Anybody knows how we could get that Javascript timer working? Or is it just impossible to prevent the browser killing the timer?

Marcus
  • 1,222
  • 2
  • 13
  • 22
  • What is the purpose of the timer? You might be better off diffing two `Date()` objects, or using [`requestAnimationFrame`](http://creativejs.com/resources/requestanimationframe/). – Quentin Feb 20 '13 at 14:29
  • 1
    Related: http://stackoverflow.com/questions/196027/is-there-a-more-accurate-way-to-create-a-javascript-timer-than-settimeout – epascarello Feb 20 '13 at 14:33
  • @Quentin: The purpose is to create some exact animation with a resolution of 10 milliseconds. I know that the graphics resolution is often only 15 milliseconds (except chrome has about 4ms), but I'll probably use about 20ms for the shortest event, so that shouldn't be any problem. The problem is that my browser doesn't allow the timer to run smoothly under certain circumstances. – Marcus Feb 20 '13 at 14:34
  • @epascarello: It actually has nothing to do with setTimeout(). The running function of the timer is a setInterval(), I'm going to change the tagging of the question... – Marcus Feb 20 '13 at 14:36
  • @Marcus — Really sounds like the usecase for [`requestAnimationFrame`](http://creativejs.com/resources/requestanimationframe/) – Quentin Feb 20 '13 at 14:37
  • JavaScript is single threaded so it is going to hang up when other things cause lock ups that is the problem here. And Quentin's comment about requestAnimationFame is probably what you want to investigate. – epascarello Feb 20 '13 at 14:49
  • @Marcus That messages is put there when someone marks it to be closed. That is what happens when your question is not 100% clear. – epascarello Feb 20 '13 at 14:53
  • @Quentin: I didn't test that framework, but the fact is: The time itself is running slower in the browser, when it's busy (I tested that with Date.now()-variants, it didn't work). So when the Date.now() runs slower, there is no workaround. Probably those frameworks were not tested in busy browsers...they'll have also a frame drop for sure. – Marcus Feb 20 '13 at 19:08
  • Finally, I ended up with a hole different solution, without a timer. Please look into my posted answer. – Marcus Feb 24 '13 at 22:48

3 Answers3

3

setInterval was never made to do timing, but to schedule execution! It's intervals are not reliable. If you want to do timing, use Date timestamps:

function Timer(){
    var start = Date.now();
    this.getTime=function() {
        return Date.now() - start;
    }
}

That way, you get 1 millisecond resolution.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • The problem is that to implement a timer, you need to have a "driving" function, it can be a setInterval or a setTimeout-recursive function. And it doesn't matter if I use Date.now(), with all solutions I still have the same problem while running an animated Gif. It looks like Javascript animation is never really reliable due to bad implementation in the browser. That's very sad, but sometimes it's good to know what is possible and what not... – Marcus Feb 20 '13 at 18:49
  • So what do you want to drive your timer? And for what would you need a "reliable" interval for animation, if the browser is already too busy to do it? – Bergi Feb 20 '13 at 18:55
  • I just thought that it's possible to do fast animations in the browser, with exact timing. But it turned out that this can't be done in Javascript. So now I need immediately call the Forefox developers to reverse their ugly Gif bugs implemented in newer Firefox. This is gonna be a mess. :( – Marcus Feb 20 '13 at 19:05
  • Fast does not necessarily need to be Exact (a kind of macroscopical [uncertainty](http://en.wikipedia.org/wiki/Uncertainty_principle)). You can do very fast animations in JS, yet you will need to let the browser do the timing for you. – Bergi Feb 20 '13 at 19:38
  • The uncertainity is exactly my problem. Sure, I found out now how to get 250 fps, but soon when the animated Gif starts, I'm getting something like 70 or 80 fps. That's because the method Date.now() has a huge uncertainity. I'm looking for a more mathematical solution now, to get at least 70fps... (*EDIT:* with "fps" I mean timer events, no real frames) – Marcus Feb 20 '13 at 20:52
  • You were somehow right with the timestamps. But still you need to put them into an interval. My final solution has an interval waiting only 2ms until it starts again, and when the date.now() matches the required time, it changes the background of my picture. But this isn't a 100% working solution for fast animations, and a 100%-solution just can't be done in Javascript. However, if you are interested in my solution, just tell me and I'm going to post it. – Marcus Feb 24 '13 at 22:20
  • Sure, you should add it as an answer to your own question so other people can profit from it (and the question can be closed) – Bergi Feb 24 '13 at 22:25
0

var timer = function(f, args) { t = Date.now(); f.call(args); console.log(Date.now() - t)}

ilan berci
  • 3,883
  • 1
  • 16
  • 21
0

Because the implementation of a timer wasn't possible, I ended up with a hole different solution, it's a kind of brut-force-running the animation within a setInterval:

function animate(elementId,pictureTimeArrayName){
    var elementStyle = document.getElementById(elementId).style;
    var pictureTimeArray = window[pictureTimeArrayName];

    function toInterval(startDate, timeSinceStart, picture){
        var interval = setInterval(function(){
            if(Date.now()-startDate>=timeSinceStart){
                clearInterval(interval);
                elementStyle.background = "url("+picture+")";
            }
        },2);
    }

    setInterval(function(){
        var startDate = Date.now();
        for(var i=0;i<pictureTimeArray.length;i++){
            toInterval(startDate,pictureTimeArray[i].time,pictureTimeArray[i].picture);
        }
    },pictureTimeArray[pictureTimeArray.length-1].time);
}

While an array looks like this:

var array_1 = [{picture: "animations/pic_1.gif", time: "30"},
               {picture: "animations/pic_2.gif", time: "60"},
               ...
               {picture: "animations/pic_36.gif", time: "1080"},];

This looks quite good (for >=30 fps). But: When an animated Gif is running, it's still not that smooth anymore. And if it is an animation repeating itself, the drawings aren't always clean. In some never browser versions (from Firefox) the drawing is even worse than in older versions, and other browsers (Chrome) have their problems, too. Especially if you change the browser window in Chrome and get back to the window: The animation will look really weird.

So you probably better use Flash or another non-Javascript-solution for your professional animations.

Marcus
  • 1,222
  • 2
  • 13
  • 22
  • …or you don't try to use animated GIFs, but do it with JS. With `requestAnimationFrame` you can get consistent high-FPS smooth animations, there is absolutely no need for Flash or Java these days. – Bergi Feb 24 '13 at 23:13
  • You should know that there are animations and animations. Animations from beginners have all the same frame rate per picture. That's nice (you can easily use requestAnimationFrame for this), but unsuitable for professional animations. For professional animations, you must choose a high framerate (about 33 fps is good) and then you need to dynamicly adapt the time for every picture (some last for 15 or 20ms, others for 40ms). That's how real animation is done. But you're certainly right about Java, applets are dead for a long time. And who's using JavaFX since more user have Flash installed? ;) – Marcus Feb 24 '13 at 23:43
  • However, if you really want to make a modern website with maximised animation for the newest browsers, you won't use requestAnimationFrame, but you'll use WebGL: http://en.wikipedia.org/wiki/WebGL – Marcus Feb 25 '13 at 00:23
  • Even WebGL animations [do use `requestAnimationFrame`](http://learningwebgl.com/blog/?p=239) for tick loops. – Bergi Feb 25 '13 at 01:16