Skip to content Skip to sidebar Skip to footer

Replacing Settimeout() With Requestanimationframe()

I am a PhD student in experimental psychology and due to COVID-19, we have to switch all our experiments online. I also don't know Javascript very well. The problem is that we usua

Solution 1:

setTimeout indeed is not in sync with the frame refresh rate, it will even have some throttling applied on it, and may be delayed by the browser if they decide an other task was more important (e.g, they may prefer to fire an UI event if it happens exactly at the same time the setTimeout was supposed to resolve, and calling it in a recursive loop will always accumulate some drift time.

So setTimeout is not reliable to animate visual content smoothly.

On the other hand, requestAnimationFrame will schedule a callback to fire in the next painting frame, generally in sync with the screen refresh rate.

requestAnimationFrame is the perfect tool to animate visual content smoothly.

But here you are not animating visual content smoothly.

The screen refresh rate we are talking about is on the vast majority of devices 60Hz, that is 16.67ms per frame. Your timeouts are set to 995ms 195ms, and 495ms. The smallest interval there (195ms) corresponds approximately to a 12Hz frequency, the biggest is almost 1Hz.

What you are doing is scheduling tasks, and for this, setTimeout is the best.

If you really need it to be as precise as possible for a long run, then incorporate a drift correction logic in your loop:

You record the starting time, then at each step, you check how much drift there was, and you adjust the next timeout accordingly:

Here is a basic example, based on your case, but it might be quite hard to get the usefulness of that drift correction on such a small sample, still pay attention on how the drift corrected version is able to reduce the drift, while in the non-corrected one, it will always add up.

const delays = [ 495, 995, 195, 995 ];

setTimeout(() => {
console.log( 'testing with drift correction' );
const start_time = performance.now();
let expected_time = start_time;
setTimeout( () => {
  // do your things hereconst now = performance.now();
  expected_time += delays[ 0 ];
  const drift = now - expected_time;
  setTimeout( () => {
    const now = performance.now();
    expected_time += delays[ 1 ];
    const drift = now - expected_time;
    setTimeout( () => {
      const now = performance.now();
      expected_time += delays[ 2 ];
      const drift = now - expected_time;
      setTimeout( () => {
        const now = performance.now();
        expected_time += delays[ 3 ];
        const drift = now - expected_time;
        console.log( 'last step drift corrected:', drift );
      }, delays[ 3 ] - drift );
      console.log( 'third step drift corrected:', drift );
    }, delays[ 2 ] - drift );
    console.log( 'second step drift corrected:', drift );
  }, delays[ 1 ] - drift );
  console.log( 'first step drift corrected:', drift );
}, delays[ 0 ] );

}, 100 );

setTimeout( () => {

console.log( 'testing without drift correction' );
const start_time = performance.now();
let expected_time = start_time;

setTimeout( () => {
  // do your things hereconst now = performance.now();
  expected_time += delays[ 0 ];
  const drift = now - expected_time;
  setTimeout( () => {
    const now = performance.now();
    expected_time += delays[ 1 ];
    const drift = now - expected_time;
    setTimeout( () => {
      const now = performance.now();
      expected_time += delays[ 2 ];
      const drift = now - expected_time;
      setTimeout( () => {
        const now = performance.now();
        expected_time += delays[ 3 ];
        const drift = now - expected_time;
        console.log( 'last step drift not corrected:', drift );
      }, delays[ 3 ] );
      console.log( 'last step drift not corrected:', drift );
    }, delays[ 2 ] );
    console.log( 'last step drift not corrected:', drift );
  }, delays[ 1 ] );
  console.log( 'last step drift not corrected:', drift );
}, delays[ 0 ] );
}, 3000 );

Post a Comment for "Replacing Settimeout() With Requestanimationframe()"