Search code examples
javascriptes6-promise

Create an loading screen for long calculation


I need to perform a huge calculation. So, I refer here to create a simple loading screen. Unfortunately, the loading screen is shown after the calculation complete.

Here is my code

class RosterScheduler
{
   .........................
   autoAssign()
   {
    var startDate=parseInt($("#autoPlannStartDate").val());
    var endDate=parseInt($("#autoPlanEndDate").val());
    $("body").addClass("loading");
    if (startDate>endDate)
        alert("Invalid start date or end date selection");
    else
    {   
        if (this.rosterTable.haveInvalidPreferredShift(startDate,endDate))
            alert("Invalid shift requirement detected");
        else
        {
            var self=this;
            var finalRoster;
            var roster,tempAverageSD,lowestAverageSD=100.0;

            this.theLowestSDRosters=[];
            this.theLowestMissingShiftRosters=[];
            for (var i=0;i<100;i++)
            {   
                this._genRoster(startDate,endDate);
            }
            console.log("Done");
        }
    }   
   } 
}

I found that if I remark the for loop, the loading screen working properly. If I uncomment the for loop, the loading screen is shown after the "Done" is shown in the console. How can I fix the problem? I have tried to convert the function _genRoster into a Promise function, however, it does not work.


Solution

  • It looks like _genRoster is blocking the browser, and not giving it any resources to re-render/repaint until the loop is completed. One possibility would be to run the loop after giving the browser a few ms to render the .loading:

    this.theLowestSDRosters = [];
    this.theLowestMissingShiftRosters = [];
    setTimeout(() => {
      for (var i = 0; i < 100; i++) {
        this._genRoster(startDate, endDate);
      }
      console.log("Done");
    }, 50);
    

    It might be more elegant to call a function with 0 timeout after a single requestAnimationFrame, thus giving the browser time to repaint and then running the heavy loop immediately afterwards:

    (warning: the following code will block your browser for a bit)

    document.body.style.backgroundColor = 'green';
    window.requestAnimationFrame(() => {
      console.log('start, setting timeout');
      setTimeout(() => {
        for (let i = 0; i < 1000000000; i++) {
        }
        console.log('done');
      });
    });