Async Animation Frames Generator

Asynchronous generator function that executes a continuous stream of user-supplied actions before the next repaint. Each time the browser is about to repaint, the requestAnimationFrame() callback is called, the promise resolves, and the generator yields the timestamp of that frame.

This allows any code using this generator to synchronize with the browser's painting cycle, it can be especially useful for tasks like measuring frame rates, synchronizing animations, or performing time-based updates.

async function* animationFrames() {
  while (true) {
    yield await new Promise(globalThis.requestAnimationFrame);
  }
}

MDN docs: https://developer.mozilla.org/en-US/docs/Web/API/Window/requestAnimationFrame

Example

async function logFPS() {
  const startTime = performance.now();
  let numberOfFrames = 0;

  for await (const timestamp of animationFrames()) {
    if (timestamp >= startTime + 1000) break;
    numberOfFrames++;
  }

  console.log("Frames Per Second: ", numberOfFrames);
}

logFPS();

When used in an asynchronous loop, animationFrames can help track how many frames are rendered over a period of time. For example, in the run function provided earlier, the for await loop iterates over the frames produced, allowing it to measure how many frames are rendered in one second.

Typewriter example

async function* typewriter(text, start = 0) {
  let index = start;

  for await (const timestamp of animationFrames()) {
    if (index < text.length) {
      const char = text[index];
      index++;
      yield char;
    }
  }
}

const text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.";

let value = "";

for await (const char of typewriter(text)) {
  value += char;
  document.body.innerHTML = value;
}