title: Is it possible to detect repaint/reflow in the browser?
body: <p>Not sure if there is a solution to this problem. I want to know if there is a way to detect when the browser <strong>actually</strong> updates the UI.</p>
  
  <p>Lets say I want add a class to an element just before I run a long-running, synchronous block of code, after the synchronous function runs, the class would be removed.</p>
  
  <pre><code>el.classList.add('waiting');
  longRunningSynchronousTask();
  el.classList.remove('waiting');
  </code></pre>
  
  <p>If I run this code, the long running task will get started, and block the UI before the class is added. I tried using <code>MutationObserver</code>'s, and checking the element with <code>getComputedStyle</code>, but that doesn't work because those don't reflect when the realtime updates the UI. </p>
  
  <p>Using a worker might be the best solution (drop the long running code in a worker and post a message when it's complete to remove the class). This isn't really an option me, due to needing to support older browsers.</p>
  
  <p>The only solution that works outside of implementing a worker is using <code>setTimeout</code>. I'd love to know if anyone has run into this problem and if there is any way to detect a repaint/reflow of the UI in the browser.</p>
  
  <p>See my example for a better illustration of the problem.</p>
  
  <p><div class="snippet" data-lang="js" data-hide="false">
  <div class="snippet-code">
  <pre class="snippet-code-js lang-js prettyprint-override"><code>const el = document.querySelector('.target');
  const isRed = c =&gt; c === 'red' || 'rgb(255, 0, 0)';
  
  let classActuallyApplied = false;
  
  let requestId;
  
  const longRunningTask = (mil, onComplete) =&gt; {
    if (classActuallyApplied) {
      cancelAnimationFrame(requestId);
      var start = new Date().getTime();
      while (new Date().getTime() &lt; start + mil);
      onComplete();
    } else {
       requestId = requestAnimationFrame(longRunningTask.bind(null, mil, onComplete));
    }
  }
  
  const observer = new MutationObserver(function(mutations) {
   classActuallyApplied = mutations
     .filter(mutation =&gt; {
     return mutation.type === 'attributes' &amp;&amp;
         mutation.attributeName === 'class' &amp;&amp;
          mutation.target.classList.contains('waiting');
    }).length &gt; 0;
  });
  
  const observerConfig = {
   attributes: true, 
   childList: false, 
   characterData: false 
  };
  
  observer.observe(el, observerConfig);
  
  el.addEventListener('click', e =&gt; {
   el.classList.add('waiting');
    longRunningTask(1000 * 5, () =&gt; {
     // el.classList.remove('waiting');
    });
  });</code></pre>
  <pre class="snippet-code-css lang-css prettyprint-override"><code>.target {
    height: 100px;
    width: 100px;
    background-color: gray;
  }
  
  .waiting {
    background-color: red;
  }</code></pre>
  <pre class="snippet-code-html lang-html prettyprint-override"><code>&lt;div class="target"&gt;
    target
  &lt;/div&gt;</code></pre>
  </div>
  </div>
  </p>