javascriptloopsoptimizationcpu-usage

How to decrease CPU usage by an extensive loop?


I am creating a script that prints an ASCII picture. I need to separate each individual character in a tag, though the page gets too laggy and the CPU usage goes up by an extreme amount when executing the loop, the process doesn't even finish, as it normally would without adding the tag (printing only the characters).

Here is the loop I used:

for(let widthCounter = 0; widthCounter <= 2500; widthCounter++) {
    document.getElementById("textMain").innerHTML += ("<span>" + currentScene[widthCounter] + "</span>");

    totalCharsDrawn += 1 

    if (totalCharsDrawn == 100) {
        document.getElementById("textMain").innerHTML += "<br>"
    }
}

I tried setting a delay for each character with setTimeout() but it didn't solve the problem.


Solution

  • First, generate the whole string, then, update the DOM.

    const currentScene = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi volutpat turpis velit, et pharetra arcu hendrerit sed. Pellentesque ultricies arcu at nulla rutrum, sit amet egestas lorem tincidunt. Pellentesque eget diam sit amet velit ultrices venenatis laoreet quis mauris. Phasellus orci lacus, condimentum nec diam in, viverra egestas lorem. Nunc ac mollis neque, gravida dignissim lorem. Fusce vel lacus vel elit consectetur varius. Etiam ultrices blandit gravida. Vestibulum quis sollicitudin risus. Maecenas vitae ante tellus. Sed tortor nunc, facilisis vitae odio auctor, dignissim semper dui. Nulla luctus est tellus, in ultricies velit malesuada et. Donec rutrum, erat vel molestie vestibulum, metus turpis volutpat elit, in feugiat purus massa in diam. In sit amet sapien libero. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vestibulum venenatis at lorem in consectetur. Proin id ex efficitur, faucibus tellus in, pulvinar urna. Nullam dapibus nibh nec nisi consequat hendrerit ac at ante. Phasellus ornare nunc ac luctus vestibulum. Sed egestas elit at mattis pretium. Aenean semper nec metus et hendrerit. Suspendisse mattis massa at nunc dapibus egestas. Phasellus a suscipit libero. Nullam dapibus dapibus tellus, vel ullamcorper neque iaculis at. Suspendisse sed dignissim ex. Praesent at dolor at enim pellentesque maximus at in massa. Donec et auctor neque. Integer eget ex est. Mauris efficitur, lectus ac pulvinar pulvinar, arcu elit convallis arcu, ut faucibus risus tortor a purus. Vestibulum id metus condimentum, scelerisque purus sed, cursus enim. Sed consequat, elit eu tempor facilisis, quam ligula luctus tortor, vitae suscipit urna lacus ac urna. Curabitur ornare nisl sed augue commodo, vitae porta ligula fringilla. Proin eget augue eu nunc luctus facilisis. Mauris dictum ante non faucibus sollicitudin. Etiam et vestibulum purus, in suscipit nisi. Sed vel consequat nisi, eget molestie arcu. Donec ornare interdum fermentum. Nunc sed lobortis nibh. Aenean at dolor ullamcorper, fringilla leo nec, semper diam. Integer mattis vitae tellus eget dictum. Etiam quis lacinia eros. Sed vitae laoreet ipsum, ac bibendum dolor. Duis at suscipit erat, ac sagittis dolor. In tempus lacus non odio iaculis, eu pellentesque tortor hendrerit. Sed magna ante, eleifend iaculis interdum id, ornare sed ipsum. Aenean ut mauris et ligula finibus finibus sit amet sed eros. Morbi hendrerit enim sit amet vehicula cursus. Curabitur faucibus urna non.";
    
    let totalCharsDrawn = 0;
    
    const start = Date.now();
    let content = "";
    for(let widthCounter = 0; widthCounter < currentScene.length; widthCounter++) {
        content += "<span>" + currentScene[widthCounter] + "</span>";
    
        totalCharsDrawn += 1 
    
        if (totalCharsDrawn == 100) {
            content += "<br>"
        }
    }
    document.getElementById("textMain").innerHTML = content;
    console.log(Date.now() - start);
    <div id="textMain"></div>

    I've added a timer to compare both approaches.

    const currentScene = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi volutpat turpis velit, et pharetra arcu hendrerit sed. Pellentesque ultricies arcu at nulla rutrum, sit amet egestas lorem tincidunt. Pellentesque eget diam sit amet velit ultrices venenatis laoreet quis mauris. Phasellus orci lacus, condimentum nec diam in, viverra egestas lorem. Nunc ac mollis neque, gravida dignissim lorem. Fusce vel lacus vel elit consectetur varius. Etiam ultrices blandit gravida. Vestibulum quis sollicitudin risus. Maecenas vitae ante tellus. Sed tortor nunc, facilisis vitae odio auctor, dignissim semper dui. Nulla luctus est tellus, in ultricies velit malesuada et. Donec rutrum, erat vel molestie vestibulum, metus turpis volutpat elit, in feugiat purus massa in diam. In sit amet sapien libero. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vestibulum venenatis at lorem in consectetur. Proin id ex efficitur, faucibus tellus in, pulvinar urna. Nullam dapibus nibh nec nisi consequat hendrerit ac at ante. Phasellus ornare nunc ac luctus vestibulum. Sed egestas elit at mattis pretium. Aenean semper nec metus et hendrerit. Suspendisse mattis massa at nunc dapibus egestas. Phasellus a suscipit libero. Nullam dapibus dapibus tellus, vel ullamcorper neque iaculis at. Suspendisse sed dignissim ex. Praesent at dolor at enim pellentesque maximus at in massa. Donec et auctor neque. Integer eget ex est. Mauris efficitur, lectus ac pulvinar pulvinar, arcu elit convallis arcu, ut faucibus risus tortor a purus. Vestibulum id metus condimentum, scelerisque purus sed, cursus enim. Sed consequat, elit eu tempor facilisis, quam ligula luctus tortor, vitae suscipit urna lacus ac urna. Curabitur ornare nisl sed augue commodo, vitae porta ligula fringilla. Proin eget augue eu nunc luctus facilisis. Mauris dictum ante non faucibus sollicitudin. Etiam et vestibulum purus, in suscipit nisi. Sed vel consequat nisi, eget molestie arcu. Donec ornare interdum fermentum. Nunc sed lobortis nibh. Aenean at dolor ullamcorper, fringilla leo nec, semper diam. Integer mattis vitae tellus eget dictum. Etiam quis lacinia eros. Sed vitae laoreet ipsum, ac bibendum dolor. Duis at suscipit erat, ac sagittis dolor. In tempus lacus non odio iaculis, eu pellentesque tortor hendrerit. Sed magna ante, eleifend iaculis interdum id, ornare sed ipsum. Aenean ut mauris et ligula finibus finibus sit amet sed eros. Morbi hendrerit enim sit amet vehicula cursus. Curabitur faucibus urna non.";
    
    let totalCharsDrawn = 0;
    
    const start = Date.now();
    for(let widthCounter = 0; widthCounter < currentScene.length; widthCounter++) {
        document.getElementById("textMain").innerHTML += "<span>" + currentScene[widthCounter] + "</span>";
    
        totalCharsDrawn += 1 
    
        if (totalCharsDrawn == 100) {
            document.getElementById("textMain").innerHTML += "<br>"
        }
    }
    console.log(Date.now() - start);
    <div id="textMain"></div>

    My code runs 1-2 ms on my system. Your code runs ~1300 ms on my system. Depending on the content of the website, the query and the update can use much time. Even with an almost empty DOM, the factor is >500. Every loop iteration, the browser has to read the whole page, find the ID and rewrite the page. In your case even twice.