I have some nested for loops that take a while to run, so I want to display a progress bar. The problem is that this is not an inherently async process, it is a block of code with 3 nested loops. I tried a slew of ways to yield so as to render the page, with and without requestAnimationFrame(), async await, and an async generator w/for await...of. The snippet below represents the only way I could get it to work.
Is there a better way to do this? One that doesn't involve calling the generator function inside the animation callback, for example.
let i, start, val;
const progress = document.getElementsByTagName("progress")[0];
function run() {
i = 0;
val = 0;
start = performance.now();
requestAnimationFrame(animateProgress);
}
function animateProgress() {
const next = loop().next();
if (!next.done) {
progress.value = next.value;
frame = requestAnimationFrame(animateProgress);
}
else
console.log(`Calculations took ${performance.now() - start}ms`);
}
function* loop() {
let j;
while (i < 100) {
for (j = 0; j < 100; j++) {
++val;
}
++i;
yield val;
}
}
* {
font-family:monospace;
font-size:1rem;
}
<button onclick="run()">Run</button>
<progress value="0" max="10000"></progress>
Run your calculations in a worker if they aren't DOM manipulations:
On the main thread (note how much it's slower, since we need to calculate and report the progress):