0

In several cases I need to wait for the browser to repaint and execute a function after. Ex: show alert after loading animation gets hidden.

This works best so far:

loader.classlist.add('hidden');
requestAnimationFrame(() => {
  setTimeout(() => {
    alert(res);
   });
});  

What also works is a simple "setTimeout()" but it's hard to determine the best amount of delay since it varies. Sometimes 10 is too long, sometimes too short. In case the above solution is best and since I'll need to call this several times for different functions: How can I wrap this into a function (async?)

function timeOut () => {
  requestAnimationFrame(() => {
    setTimeout(() => {
      return true;
    });
  }); 
}

timeOut(); if timeOut returns true then go on, alert(res) or execute other function

mbe
  • 11
  • 4
  • 1
    Why kind of "animation" are we talking about here? A pure CSS animation? Then you should be working with the event handlers available with those. Some animation library? That probably emits events for that kind of stuff as well. – CBroe May 12 '23 at 11:28
  • If you ask me a setTimeout with 0 delay should always work for repaints because the only thing you want to wait for is the rest of the callstack. If not, a timeout is not your friend and you should look into callbacks or eventhandlers like CBroe said. – Randy May 12 '23 at 11:31
  • I'll need this function in different cases. The animation is css based, (a simple infinite loader spin) and gets hidden by adding a css class. (It could also be just a div that gets hidden by adding a css class). In case of the alert a simple setTimeout with "0 delay" did not work. (at least on Safari/Webkit) – mbe May 12 '23 at 11:37
  • @Randy - `0` isn't long enough. Repaint happens every 16.66ms on 60Hz monitors (lots of folks still have them). For those, to be sure enough time had passed for the repaint to happen, you'd have to use `17` -- and even then, it's a bit dodgy vs. asking the browser to **tell** you when it is about to happen as the OP does. :-) – T.J. Crowder May 12 '23 at 11:45
  • Fundamentally, if you mean you want a function returning a promise that will be fulfilled when the timer inside that `requestAnimationFrame` callback is done, you're asking the same thing (basically) as [this question](http://stackoverflow.com/questions/22707475/how-to-make-a-promise-from-settimeout/22707551#22707551). See its answers. You just have the extra `requestAnimationFrame` part to put inside the promise executor. – T.J. Crowder May 12 '23 at 11:47
  • 1
    Thank's @T.J.Crowder, didn't see that one. – mbe May 12 '23 at 11:50
  • @T.J.Crowder what am I doing wrong?: `function timeOut() { return new Promise(res => { requestAnimationFrame(() = { setTimeout(res);});});} timeOut().promise .then(console.log('repaint'));` – mbe May 12 '23 at 12:54
  • You're **calling** `console.log('repaint')` immediately and passing its return value (`undefined`) into `then`. You want to pass in a *function*: `.then(() => console.log("repaint"))`. More: https://stackoverflow.com/questions/15886272/what-is-the-difference-between-a-function-call-and-function-reference Also, there's no `promise` property. You call `.then` directly on the promise returned by `timeOut()`. (There are usage examples in [the answers](http://stackoverflow.com/questions/22707475/how-to-make-a-promise-from-settimeout/22707551#22707551) to that question). – T.J. Crowder May 12 '23 at 12:56
  • @mbe - What my comment above didn't say and should have was: Nice one on the rest of it, though! – T.J. Crowder May 12 '23 at 14:03
  • For what it's worth, there used to be a [requestPostAnimationFrame](https://github.com/WICG/request-post-animation-frame/blob/main/explainer.md) proposal, but it's been abandoned apparently. Still, [I made a polyfill for this Q/A](https://stackoverflow.com/questions/50895206/exact-time-of-display-requestanimationframe-usage-and-timeline/57549862#) that you might be interested in. Doesn't return a Promise, but gets you as close to the paint as possible. – Kaiido May 18 '23 at 10:00

0 Answers0