skip to Main Content

I found a couple of questions pretty close to what I am looking for, but either I don’t understand enough or the questions/answers do not exactly apply to what I am looking for:

Consider a very simple example of a requestAnimationFrame loop:

const someList = [];

function main() {
    // Start the requestAnimationFrame loop
    requestAnimationFrame(update)
}

function update() {
    doSomething()

    // Loop by calling update again with requestAnimationFrame
    requestAnimationFrame(update);
}

function doSomething() {
    someList.push(Math.floor(Math.random() * 10));
}

main();

Now here we do not have any problems. We start the loop, do something within the loop, call requestAnimationFrame again and everything is fine.

Now consider this next example. Here we have an async function called doSomethingAsynchronous and it has to be called with await. Therefore our update function needs to be async as well. My problem is that I do not understand how to work with requestAnimationFrame when the callback-function passed to the requestAnimationFrame is asynchronous:

function main() {
    // Since update must now be async as well and called with await, what should this line look like?
    requestAnimationFrame(update)
}

function async update() {
    await doSomething()

    // Since update must now be async as well and called with await, what should this line look like?
    requestAnimationFrame(update);
}

function async doSomethingAsynchronous() {
    // Can't come up with an example, but this function should be called with await
}

main();

I feel like the requestAnimationFrame line in the code should probably look like one of these:

await requestAnimationFrame(update)
await requestAnimationFrame(async () => { await update(); });
requestAnimationFrame(async () => { await update(); });

But I’m not exactly sure if requestAnimationFrame can or should be awaited. In case I need to supply the update callback as an async function, that is also something I can’t wrap my head around.

Edit: In case someone is wondering why I am using requestAnimationFrame, the reason is that I am using TensorflowJS Object Detection model which returns the detection results asynchronously. So I want to asynchronously get the results and then render and visualize the results of a canvas – thus the requestAnimationFrame.

2

Answers


  1. If you want to make async rendering:

    let running = true;
    async function update(cb) {
      while(running){
        await cb();
      }
    }
    let counter = 0;
    async function doSomething() {
        await new Promise(r => setTimeout(r, 100));
        requestAnimationFrame(()=>$div.textContent = counter++);
    }
    
    update(doSomething)
    <div id="$div"></div>
    <button onclick="running=false">Stop</button>
    Login or Signup to reply.
  2. I don’t see any problem with the code you propose yourself, but as you have not included the rendering code, make sure that it is executed at the top of your update function, so that once update is called, it is executed synchronously.

    I would however suggest to promisify requestAnimationFrame like this:

    const nextFrame = () => new Promise(requestAnimationFrame);
    

    This looks cryptic, but it is short for:

    const nextFrame = () => new Promise(resolve => requestAnimationFrame(resolve));
    

    …but without creating the arrow function wrapper.

    And then you can write an async while loop where you await both your own promise and the nextFrame one:

    const nextFrame = () => new Promise(requestAnimationFrame);
    
    async function main() {
        let counter = 0;
        while (true) { // An async loop
            // Rendering code comes here: some simple counter example
            //      ....
            document.querySelector("div").textContent = counter++;
            //
            // After rendering code, immediately call the async function
            await doSomething();
            // If you really want to, await the start of the next paint cycle
            await nextFrame();
        }
    }
    
    // Dummy implementation:
    const doSomething = () => new Promise(resolve => setTimeout(resolve, 100));
    
    main();
    <div></div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search