JavaScript requestAnimationFrame() simplified
Tags: javascript, asynchronous, webdev
Published: 2021-02-28 09:24:27

There is a little bit of mystery for `requestAnimationFrame()`, and it can be very simple.

The name could actually be `addScreenRefreshListener()` or `addPageUpdateListener()`.

And that means, "hey screen, (and the DOM display engine), when you are about to actually show the DOM accurately on screen, notify me so that I can now update the DOM to the most appropriate values.  This is so that I don't need to update it 2, 3, or 4 times, all wasting the effort because it is actually not being shown on the screen."

Note that when we do `element.innerHTML = "something"` or `element.appendChild(element2)`, it doesn't get drawn on the screen.  Only the DOM tree structure is modified and it is not displayed on screen.  To draw on the screen, the JS engine in a browser will traverse the DOM tree and draw each element on screen as needed, at a certain clock tick, typically at every 1/60 of second interval, so at about every 16.67ms.

So that's the most basic idea.  Try to re-read the above statement if it is confusing at first.

So you can think of `requestAnimationFrame()` as a `setTimeout()` or `setInterval()`, only this time, you don't specify the time delay or the interval.  The system decides that for you, so that it happens *right before* it is going to show your updates on screen.

Think of a stopwatch app.  If you show your time accurate to 1/100 of a second. Let's say the JS engine already repainted the DOM tree at 030/1000 of the second.  If you set the DOM at 032/1000, and then at 042/1000, it is really useless if the JS engine doesn't show any of that on the screen, isn't it?

So you may as well wait until the JS engine notifies you, at 046/100, so that your code updates the DOM at 046/1000 and then the JS engine shows it on screen. 

This way, there is no effort wasted.

Note that `requestAnimationFrame()` is a one-time thing only, which means the listener will be called one time only.  It is similar to a `setTimeout()`, and if we'd like our listener to be notified repeatedly, we need to call `requestAnimationFrame()` again.

The usual way to use it:

```js
function myAwesomeFunctionToUpdateTheDOM(ts) {
  // do the update
  // ...

  // Now request it again (if needed)
  requestAnimationFrame(myAwesomeFunctionToUpdateTheDOM);
}

requestAnimationFrame(myAwesomeFunctionToUpdateTheDOM);
```

So we 

1. define a function to do: (a) the proper update and (b) add the listener again
2. now we kick start this function by telling it to be notified in the next screen refresh

The `ts` given to your update function is a timestamp, in millisecond (ms), and is guaranteed to have the minimum resolution of 1ms (but can be at a higher resolution).

One thing to take note is, if you want the stopwatch to be really accurate, you should make sure that when the user clicks on the Stop (or Pause) button, then at the next repaint, it should not paint the `Date.now()` time at the screen repaint, but instead paint the time when the user clicked the Stop button.

The ultimate take aways is, instead of using `setInterval()` to update the screen more often than needed, update it only *when* needed.