1

Is it possible to force the browser to perform immediate reflow & repaint from JavaScript, even if there is another code running?

I am rendering a progress bar, everything based on async events so when something loads from server or from cache the DOM updates, but sometimes it is that fast the progress bar stills at i.e. 10% then it is replaced with the regular DOM document.

I tried all that tricks with display/visibility none/hidden, getComputedStyle, requestAnimationFrame but nothing forces the browser to do a real repaint. It probably flushes the queue and applies all the changes but no real repaint to screen occurs.

Fis
  • 787
  • 2
  • 10
  • 24
  • Forgot to mention that with breakpoint after DOM update it works fine. – Fis Jan 30 '17 at 21:48
  • 1
    Possible duplicate of [Can I use javascript to force the browser to "flush" any pending layout changes?](http://stackoverflow.com/questions/6955912/can-i-use-javascript-to-force-the-browser-to-flush-any-pending-layout-changes) – Heretic Monkey Jan 30 '17 at 22:06
  • @Fis You can [edit] your question at any time. – Sebastian Simon Jan 30 '17 at 22:23

2 Answers2

0

JavaScript runs in a single-threaded execution environment. So no, you can't stop a running execution context and do something else.

Take this code snippet for example... Will the paragraph's text update before the alert appears?

function foo(){
  
  // Note that this timer function is set to run after a zero millisecond delay
  // which effectively means immediately. But, it won't do that because it must
  // first finish executing the current function. So, the timer function will be
  // placed in the event queue and will run as soon as the JS engine is idle. But,
  // we can't know exactly when that will be, we can only ask to run the code after
  // a "minimum" delay time.
  setTimeout(function(){
    document.querySelector("p").textContent = "I've been updated by JS!";
  }, 0);
  
  // The alert will run before the setTimeout function because the alert is part of the
  // current execution context. No other execution context can run simultaneously with this one.
  alert("While you are looking at me, check to see if the paragraph's text has changed yet.");
}

document.querySelector("button").addEventListener("click", function(){
  foo();
});
<button>Click Me</button>
<p>Some content that can be updated by JS</p>
Scott Marcus
  • 64,069
  • 6
  • 49
  • 71
  • sometimes the DOM gets updated and screen is re-rendered so I don't think this is correct answer. – Fis Jan 30 '17 at 21:58
  • 1
    @Fjs It is the correct answer. You cannot have JavaScript execute while another execution context is running. This is the premise behind the "event queue" and callbacks. – Scott Marcus Jan 30 '17 at 22:00
  • Yes and no. As I said, the repaint sometimes occur, sometimes not even if the javasript is running. – Fis Jan 30 '17 at 22:04
  • Maybe there is some event (ie. xhr is running or whatever else) causing the browser to repaint. – Fis Jan 30 '17 at 22:05
  • Actually, just no. The way a JavaScript engine runs is defined by the spec. and is not dependent on the implementation. What you are experiencing is most-likely just differences in the speed with which code runs. Anything can cause code to run faster or slower. See my updated answer for an example. – Scott Marcus Jan 30 '17 at 22:06
  • But in my case it behaves bit different as it is completely async. Can't explain what is going on there. – Fis Jan 30 '17 at 22:08
  • It's funny because "async" is a bit of a misnomer in JavaScript. As I've said, the JavaScript execution environment is single-threaded, so technically there is no such thing as "async" in that sense. However, the user agent itself can do operations asynchronously (operations that run apart from the JavaScript runtime, like making HTTP requests for example). So, while the browser goes off and does something while the JavaScript engine is plugging away on something else, all the browser can do is sit and wait until the JS engine is ready for a task. That's what the event queue is for. – Scott Marcus Jan 30 '17 at 22:16
0

So in the end, setTimeout(resolvePromise(...), 0) helped. It gives a browser some small amount of time to repaint.

Fis
  • 787
  • 2
  • 10
  • 24