9

In the mozilla docs on performance best practices for front-end engineers it's recommended to combine setTimeout with requestAnimationFrame to run something immediately after a repaint:

requestAnimationFrame(() => {
  setTimeout(() => {
    // This code will be run ASAP after Style and Layout information have
    // been calculated and the paint has occurred. Unless something else
    // has dirtied the DOM very early, querying for style and layout information
    // here should be cheap.
  }, 0);
});

Why is this the recommended solution? What exactly makes this the optimal way to schedule something right after a repaint?

  • The DOM is not recalculated whe you set new (layout)values to it, the recalculation occurs when you read values after changing the DOM, or just before it is repainted. The timed function gives you most likely an unchanged DOM, i.e. it is readily calculated, and reading a value doesn't cause recalculation, because the timed function will be called immediately after the repaint. – Teemu Nov 08 '17 at 16:08
  • "querying for style and layout information here should be cheap." no page re-flow, calculation, etc... – Peter Nov 08 '17 at 16:09

1 Answers1

6

Why is this the recommended solution? What exactly makes this the optimal way to schedule something right after a repaint?

Running code immediately after a repaint maximizes the chance that the DOM has been fully calculated, and thus minimizes the chance that querying the dom will cause a costly reflow. If you're not querying the dom anyway, then this isn't something you need to worry about.

requestAnimationFrame will schedule code to run immediately before the repaint, which is close to where you want to be but not quite. So then doing a setTimeout of 0 (or setImmediate on browsers that support it) will execute code as soon as it can after that. This won't guarantee that your code is the first thing to run after the repaint, but it's the best you can do given the apis at your disposal.

Nicholas Tower
  • 72,740
  • 7
  • 86
  • 98
  • You say that `requestAnimationFrame` will schedule code to run immediately before the repaint, but it might call the provided callback multiple times in the same frame right? How can it call the cb multiple times in the same frame and still guarantee to run right before repaint? –  Nov 08 '17 at 16:15
  • if you only call `requestAnimationFrame` once, then you should only get one callback. – Nicholas Tower Nov 08 '17 at 16:17
  • Yeah but if you recursively call it. –  Nov 08 '17 at 16:18
  • If you're in a requestAnimationFrame callback and you make another call to requestAnimationFrame, that second call will happen on the *next* frame, not the current one. – Nicholas Tower Nov 08 '17 at 16:21
  • Well mdn states: `Multiple callbacks in a single frame, therefore, each receive the same timestamp even though time has passed during the computation of every previous callback's workload.` https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame –  Nov 08 '17 at 16:24
  • That's relevant if you make multiple calls to requestAnimationFrame all before the frame goes off. Making a call to requestAnimationFrame while inside a requrestAnimationFrame callback should not cause this. – Nicholas Tower Nov 08 '17 at 16:26
  • I understand that as: the callback might be called multiple times in the same frame, if you recursively call requestAnimationFrame. Or am I misunderstanding? Because in that case how can execution right before repaint be guaranteed? –  Nov 08 '17 at 16:26
  • As i said, calling requestAnimationFrame while inside a requestAnimationFrame callback will wait until the *next* frame. You will only get callbacks one at a time. – Nicholas Tower Nov 08 '17 at 16:27
  • Ah ok, I understand now. They're talking about calling requestAnimationFrame multiple times, but not when inside the callback. Just multiple unrelated calls. Thanks for explaining, it makes sense now. –  Nov 08 '17 at 17:31