I have a Javascript function that requires a bit of time to be processed. During the execution of this JS function I would show some text (and/or apply a modal overlay) and then after the execution show the results on the page. It happens that only the final text is shown and all text modification during the process are not. So basically the question is: why only the last DOM modification is visible/applied and all other previous modification not? Follows a small example to explain simply what I mean.

<!DOCTYPE html>
<html>
    <head>
        <style>
            #modal-overlay{position: absolute;left: 0; top: 0; right: 0; bottom: 0;z-index: 2;background-color: rgba(255,255,255,0.7);}
            #modal-overlay-content {position: absolute; transform: translateY(-50%); -webkit-transform: translateY(-50%); -ms-transform: translateY(-50%); top: 50%; left: 0; right: 0; text-align: center;}
        </style>
        <script>
            function test() {
                step1();
                step2();
                step3();
            }

            function step1() {
                document.getElementById("modal-overlay").style.display = "inline";
                document.getElementById("paragraph").innerText = "Do you see this text?";
            }

            function step2() {
                // do nothing for 5000ms
                var now = new Date().getTime();
                while (new Date().getTime() < now + 5000) { /* do nothing */ }
            }

            function step3() {
                document.getElementById("modal-overlay").style.display = "none";
                document.getElementById("paragraph").innerText = "Only this text is shown at the end of the process";
            }
        </script>
    </head>
    <body>
        <div id="modal-overlay" style="display: none;"><div id="modal-overlay-content"><h1>Loading...</h1></div></div>
        <p id="paragraph"></p>
        <input type="submit" value="Push" onclick="test()" />
    </body>
</html>

2 Answers

0
apple apple On

you should use setTimeout to schedule function, not busy waiting.

function test() {
    step1();
    setTimeout(step3,5000)
}

function step1() {
    document.getElementById("modal-overlay").style.display = "inline";
    document.getElementById("paragraph").innerText = "Do you see this text?";
}

function step3() {
    document.getElementById("modal-overlay").style.display = "none";
    document.getElementById("paragraph").innerText = "Only this text is shown at the end of the process";
}
<html>
    <head>
        <style>
            #modal-overlay{position: absolute;left: 0; top: 0; right: 0; bottom: 0;z-index: 2;background-color: rgba(255,255,255,0.7);}
            #modal-overlay-content {position: absolute; transform: translateY(-50%); -webkit-transform: translateY(-50%); -ms-transform: translateY(-50%); top: 50%; left: 0; right: 0; text-align: center;}
        </style>
        <script>

        </script>
    </head>
    <body>
        <div id="modal-overlay" style="display: none;"><div id="modal-overlay-content"><h1>Loading...</h1></div></div>
        <p id="paragraph"></p>
        <input type="submit" value="Push" onclick="test()" />
    </body>
</html>


let's say, unless you have good reason, do not use fixed duration loading modal. Load you resource, and register a callback for it's completion.

0
Robin Zigmond On

The correct way to do what you want is as follows:

function test() {
    step1();
    setTimeout(step3, 5000);
}

function step1() {
    document.getElementById("modal-overlay").style.display = "inline";
    document.getElementById("paragraph").innerText = "Do you see this text?";
}

function step3() {
   document.getElementById("modal-overlay").style.display = "none";
    document.getElementById("paragraph").innerText = "Only this text is shown at the end of the process";
}

The difference is that setTimeout is "asynchronous". It allows other code to keep running, and comes back to execute the function you provided it with (here step3) after the desired time interval has passed.

Your original version attempts to "wait" for 5 seconds in a "synchronous" manner - by basically forcing the code to execute an empty loop, and check the timestamp at each iteration, until enough time has passed. During this time, code is being executed - code that has no observable effect in itself, but nevertheless code is still being executed. Since Javascript is single-threaded, this stops anything else from happening on the page. In particular, it stops DOM updates from happening, which is why your code wasn't behaving as you expected.

Even more seriously though, it prevents users interacting with the browser at all - whether they're trying to click something, scroll the page, or whatever, the browser would appear to be completely unresponsive. And that is the main reason why you should always use asynchronous functions for such thing, rather than create an artificial loop to stop anything happening.