progress bar in for loop

2019-02-27 13:24发布

问题:

I need a progress bar in a for loop in javascript

for(index=0; index < 1000; index++){

// set progress bar value to  index/1000 * 100 + '%'

}

I notice we can put it in a setInterval function, but it will cost more time to run the for loop.

So is there any way to run the for loop and generate the progress bar without cost more time?

回答1:

So is there any way to run the for loop and generate the progress bar without cost more time?

Mostly no (see below for why "mostly"), you have to yield back to the browser (e.g., via setTimeout or setInterval) so it can update the page display, and that will indeed increase the amount of time the loop runs. Now, typically if you use a timeout of 0, browsers will call you back in between 5 and 10 milliseconds. So call it 10ms. In your 1,000-cycle loop, you might yield every 100 cycles (e.g., 10 progress updates), which would only add 100ms or so to the total time.

FWIW, that looks like this (let's say it's an array you're looping through):

function loop(array, batchSize, callback) {
    var index = 0;

    doWork();

    function doWork() {
        var limit = Math.min(array.length, index + batchSize);
        while (index < limit) {
            // Do something with array[index]
        }
        if (limit < max) {
            setTimeout(doWork, 0);
        }
        else {
            callback(); // Done
        }
    }
}

Use:

loop(someArray, 100, function() {
    // Done
});

Why "mostly" no: On some browsers (not, notably, IE9 or earlier), for some tasks, you could use web workers. A web worker is an isolated JavaScript thread, separate from the main JavaScript thread, which runs in the background. The two threads communicate by posting messages to each other.

So you could fire up a web worker, hand it the work, and have it post you messages to update your progress bar. This article gives the basics of web workers, if you want to go that route.

For what it's worth, it looks something like this:

Main document and script:

<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Progress</title>
<style type="text/css">
body, html {
  height: 100%;
}
body {
    font-family: sans-serif;
}
</style>
</head>
<body>
<div id="progress"><em>(Click to start)</em></div>
<script>
(function() {
  var div = document.getElementById("progress");
  var counter = new Worker("counter.js");

  div.addEventListener("click", run);

  counter.addEventListener("message", function(event) {
    div.innerHTML = "Counter so far: " + event.data.counter;
  });

  function run() {
    div.removeEventListener("click", run);
    counter.postMessage({ max: 10000000 });
  }
})();
</script>
</body>
</html>

counter.js web-worker script (they're always separate files):

self.addEventListener("message", function(event) {
  var max;
  var counter;

  max = event.data && event.data.max || 100000;
  for (counter = 0; counter < max; ++counter) {
    if (counter % 1000 === 0) {
      self.postMessage({counter: counter});
    }
  }
  self.postMessage({counter: counter});
});