FZs
FZs

Reputation: 18619

Progressbar does not update while FOR running JS

I'm creating a progam, where JavaScript is processing a huge amount of data, so I want to show the progress on a progressbar.

The problem comes here: While the for loop is running, the progressbar does not update, and then it will be fullfilled immediately.

document.getElementById("start").addEventListener("click",function(){
    max=1000000
    document.getElementById("pb").max=max
    for(i=0;i<max;i++){
        document.getElementById("pb").value=i+1
    }
    console.log("Finished")
})
<progress id="pb" value="0" max="0"></progress>
<input type="button" id="start" value="Start"/>

How can I solve that problem?

I don't want to use any JS library, if it's possible without them.

Thanks for any help!

Upvotes: 2

Views: 602

Answers (2)

Keith
Keith

Reputation: 24191

Here is a way of doing this async / await way,.. So you can keep your for loop.

Also notices I've reduced the max to 1000, as waiting 4 and half hours might be overkill. :)

function sleep(ms) { return new Promise((r) => 
  setTimeout(r, ms)); }
  
async function run() {
  let max=1000;
  document.getElementById("pb").max=max;
  for(let i=0;i<max;i++){
    document.getElementById("pb").value=i+1
    await sleep(0);
  }
  console.log("Finished")
}

document.getElementById("start").
  addEventListener("click", run);
<progress id="pb" value="0" max="0"></progress>
<input type="button" id="start" value="Start"/>

Update, the above snippet was deliberately not optimized to keep things simple. But one issue with the above the sleep(0) is called way more often than required, as such I've created a slightly more complex example that processes some CPU intensive task, and how to handle this.

function sleep(ms) { return new Promise((r) => 
  setTimeout(r, ms)); }
  
function doSomeWork() {
  //wait about 5ms
  const st = Date.now();
  while (Date.now() - st < 5);
}
  
async function run() {
  let max = 1000;
  let lTm = Date.now();
  const pb = document.getElementById("pb");
  document.getElementById("pb").max=max;
  for(let i=0;i<max;i++){
    doSomeWork();
    const tm = Date.now();
    if (tm - lTm > 100 || i === max -1) {
      pb.value=i+1;
      await sleep(0);
    }
  }
  console.log("Finished")
}

document.getElementById("start").
  addEventListener("click", run);
<progress id="pb" value="0" max="0"></progress>
<input type="button" id="start" value="Start"/>

Upvotes: 1

rafaelcastrocouto
rafaelcastrocouto

Reputation: 12161

You can use requestAnimationFrame:

document.getElementById("start").addEventListener("click",function(){
    var max=100;
    var p=0;
    document.getElementById("pb").max=max;
    var update = function () {
      document.getElementById("pb").value=p; 
      p++;
      if (p <= max) requestAnimationFrame(update);
      else console.log("Finished");
    };
    update();
})
<progress id="pb" value="0" max="0"></progress>
<input type="button" id="start" value="Start"/>

Or just use setTimeout and increase the timer:

document.getElementById("start").addEventListener("click",function(){
    max=100;
    document.getElementById("pb").max=max;
    for(i=0;i<max;i++){
        setTimeout(function () {
          document.getElementById("pb").value=this;
          if (this == max) console.log("Finished")
        }.bind(i+1), i*16);
    }
})
<progress id="pb" value="0" max="0"></progress>
<input type="button" id="start" value="Start"/>

Upvotes: 2

Related Questions