JS/HTML:更新异步块内的进度条

Fin*_*ers 2 javascript asynchronous

我目前正在开发一个基于 html 的小型项目,其中我在 javascript 中进行了一些长时间运行的计算。我不擅长 javascript,所以我想在这里找到解决方案。基本上我的长时间运行的代码看起来像这样:

let simulation = new Simulation(system,iterations,step_size,callback_fn);
simulation.run();

Run Code Online (Sandbox Code Playgroud)

simulation.run-function 看起来像这样:

run() {
    ...
    for(let i = 0; i < this.steps; i++){
        ...
        this.callback(i+1);
    }
}
Run Code Online (Sandbox Code Playgroud)

通常该steps变量约为 100 万。我的计划是添加一个显示进度的进度条。

HTML:
<div class="progress mt-5">
    <div id="simulation-progress" class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" style="width: 0%"></div>
</div>
Run Code Online (Sandbox Code Playgroud)

现在我的计划是使用我正在模拟的回调函数来更新我的进度条:

(async () => {
    const iterations = 1000000;
    const step_size  = 1;

    simulation_progress.setAttribute('style', `width:0%`);

    setTimeout(function(){
        let simulation = new Simulation(
            system,
            iterations,
            step_size,
            e => {
                if(e % 100000 === 0){
                    simulation_progress.setAttribute('style', `width:${Math.round(100 * e / iterations)}%`);
                    console.log(e, Math.round(100 * e / iterations))
                }
            });
        simulation.run();

    }, 1);
})();
Run Code Online (Sandbox Code Playgroud)

基本上我知道大约 100000 步大约等于 1 秒。每当我的回调返回的步骤是 100000 ( ) 的倍数时steps % 100000 === 0),我都会尝试更新进度条。遗憾的是,这似乎不起作用。所发生的情况是,进度条不会更改,直到模拟完成其 1M 步骤。之后1M步,直接跳到100%。

有人知道是什么原因导致这种行为吗?它是否与更新该异步块内的进度条有关?

我很高兴能得到任何帮助

do-*_*-me 5

使用 webworker.js 或 setInterval() 的两种可能的解决方案

根据您想要添加的复杂程度,有两种方法可以使其发挥作用。

1.Webworker.js

这是“正确”的解决方案。繁重的工作被外包给 webworker.js 文件,因此 UI 和进度条不会同时冻结。

简单的例子:

<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
  <title>Async Progress Bar Example</title>
</head>
<body>
  <progress id="progressBar" value="0" max="100"></progress>
  <script src="main.js"></script>
</body>
</html>
Run Code Online (Sandbox Code Playgroud)
// main.js
const worker = new Worker('webworker.js');

worker.onmessage = function(e) {
  const progressBar = document.querySelector('#progressBar');
  progressBar.value = e.data;
}

async function asyncFunction() {
  // simulate a long running task
  for (let i = 0; i < 100; i++) {
    await new Promise(resolve => setTimeout(resolve, 50));
    worker.postMessage(i);
  }
}

asyncFunction();
Run Code Online (Sandbox Code Playgroud)
// webworker.js
onmessage = function(e) {
  postMessage(e.data + 1);
}
Run Code Online (Sandbox Code Playgroud)

2.设置间隔()

如果您希望使其尽可能简单(例如在一个文件/脚本中)并且您不关心 UI 冻结而只是希望进度条正确更新,请考虑此解决方案。

简单的例子:

<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
  <title>Async Progress Bar Example</title>
</head>
<body>
  <progress id="progressBar" value="0" max="100"></progress>
  <script src="main.js"></script>
</body>
</html>
Run Code Online (Sandbox Code Playgroud)
// main.js
let progress = 0;
const progressBar = document.querySelector('#progressBar');

async function asyncFunction() {
  // simulate a long running task
  for (let i = 0; i < 100; i++) {
    await new Promise(resolve => setTimeout(resolve, 50));
    progress++;
  }
}

setInterval(() => {
  progressBar.value = progress;
}, 0);

asyncFunction();
Run Code Online (Sandbox Code Playgroud)

请注意,0 毫秒的间隔应该足以更新进度条。就我而言,它工作得很好。此外,用户界面并没有完全冻结,但浏览器每次迭代都会响应滚动或类似的操作。它有点滞后,但对于我的用例来说已经足够了。您还可以将其与灰屏覆盖层结合起来,这样在计算准备就绪之前,用户就不会做任何事情。