Hellma
Hellma

Reputation: 167

requestAnimationFrame API - Animate during intensive JavaScript Function

I have a long taking, thread-blocking function in a web app. During its execution, I would like to show an animation icon. I want to use Window.requestAnimationFrame(). The sources I have worked with are:


I understand, that i need a function - i call it animateLoadingIcon() - , to update my drawing. In its function body, i must finally call requestAnimationFrame(animateLoadingIcon) to start the recursive update of the view.

Find below the function animateLoadingIcon, which, based on the time passed, since it was called, calculates the rotation angle of the icon.

function animateLoadingIcon() {
rotateCount = (new Date().getTime() - starttime) / 3;
if (rotateCount > 359) {
  rotateCount %= 360;
}
divLoadingIcon.style.transform = 'rotate(' + rotateCount + 'deg)';
requestedAnimationFrame = requestAnimationFrame(animateLoadingIcon);

}

The function, I originally want to execute is calc(), where animateLoadingIcon() is called.

function calc() {

let randomMills = Math.random()*6000;
console.log("Start with duration: " + randomMills +"ms.")

starttime = new Date().getTime();
let recentTime = new Date().getTime();

let i = 0;

animateLoadingIcon();   
while (recentTime < starttime+randomMills) {
    i++;
    isPrime(i);
    recentTime = new Date().getTime();

} 
cancelAnimationFrame(requestedAnimationFrame);    

}

I expect the wheel to turn, when I click on the button and to stop, when the while-loop is left. Yet, it does not. Can someone please point out my mistakes?

Please find a jsfiddle example below https://jsfiddle.net/71vqarLs/8/

Upvotes: 1

Views: 667

Answers (2)

Hellma
Hellma

Reputation: 167

Thanks for all the useful input. The hints on outsourcing the thread-blocking while-loop into a worker were on point.

I basically now only call

function calc() {

    animateLoadingIcon();   
    worker.postMessage([randomMills, starttime])

}

and when the worker emits his event for being ready, i execute:

worker.onmessage = function (e) {
    cancelAnimationFrame(requestedAnimationFrame);
}

This puts the blocking code into a separate thread, while requestAnimationFrame can peacefully run in the main thread.

Upvotes: 1

user120242
user120242

Reputation: 15268

WebWorkers would be a better solution for this, and would be the "right" way to do this.

Using Promise and async/await, using a setTimeout loop instead of a while loop, so that each call to isPrime and each loop iteration is deferred in the event loop, and allows the animation to execute in between.

jsfiddle demo: https://jsfiddle.net/cxews089/

  await new Promise(res => {
    var loop = () => {
      var tid = setTimeout(() => {
        if (recentTime < starttime + randomMills) {
          i++;
          isPrime(i);
          recentTime = new Date().getTime();
          loop()
        } else {clearTimeout(tid);res();}
      }, 0)
    };
    loop()
  });

Not too pretty and definitely can be written more elegantly, but it works. Probably an async function loop would also work as a one-liner equivalent.
There will of course be overhead, and you may want to do chunking of calls to isPrime or whatever else you are calling.

Upvotes: 2

Related Questions