【问题标题】:Make a progress bar inside inner loop - Javascript在内部循环中制作进度条 - Javascript
【发布时间】:2017-12-19 23:07:06
【问题描述】:

我有一个带有 for 循环的项目,大约需要 10 分钟才能完成。

这是一个html画布操作效果,仅供个人使用,所以我不太在意速度。

但是,如果我可以添加一个进度条,这样我就可以知道程序通过循环走了多远。

问题是我尝试过的任何东西似乎都在循环结束之前更新。

例如考虑下面的代码:

var updateDiv = document.getElementById("update"); //Get a div to write progress to

for(i=0;i<10000;i++){ 

  //super crazy code
  updateDiv.innerHTML = i;

}

使用此代码,在循环完全完成之前,内部 html 不会写入 div。所以它不能用作更新功能。

我也试过console.log(i),效果一样。

我发现在循环执行期间将显示的唯一 javascript 功能是“警报”,它会暂停循环执行并强制您单击一百万次以通过内部循环。所以基本上是不行的。

有什么方法可以显示 for 循环的进度吗?

【问题讨论】:

  • @Wordica 相信我,它做了很多工作。 :D 我已经在使用类型化数组之类的了。它可能会更快,但不会快很多,而且 10 分钟运行良好 :)
  • 为什么有人删除我的评论? @zakaria acharki 吗?如果您使用数组,那么您可以创建二维数组并更新索引......然后创建预加载器查看数组进度。

标签: javascript html loops


【解决方案1】:

它没有改变的原因是因为没有页面重绘。没有页面重绘的原因是因为 JavaScript 在任务队列中工作。

由于您的 for 循环当前正在通过使用操作系统 100% 的可用功率(实际百分比取决于浏览器和操作系统)来最大限度地利用处理器,因此不允许运行其他任务直到 for 循环完成 - 此时请求的页面重绘将运行(同时更新您的图形)。

您真正应该做的是使用 requestAnimationFrameMDN 来完成这项工作,而不是使用一个非常大的 for 循环。这将允许您为动画、监控以及您将来可能想出的任何其他内容提供各种其他钩子。

【讨论】:

  • @Travis J,感谢您的快速回复。是的,通过所有给定的答案,我知道我必须以异步方式运行代码,而 requestAnimationFrame 是最有效的方法。我确实注意到的一件事是,我在函数之后的所有代码(过去是按顺序运行的)现在在循环完成之前运行,但这就是异步的工作方式。
  • @YAHsaves - 是的,引入任何类型的异步都意味着重构代码的其他部分(如果它是顺序的)。尽管在那种情况下,您可以使用承诺或回调以使其看起来仍然是连续的。
【解决方案2】:

避免同步进行。这会锁定浏览器并且不允许任何其他事情发生。不会发生抽奖循环,而且看起来没有任何变化。

尝试将它分解为一个只执行一个步骤的函数,然后通过超时一遍又一遍地调用它。

var updateDiv = document.getElementById("update");
var i = 0;
var timeout;

function stopProcessing() {
  if(timeout) {
    clearTimeout(timeout);
  }
}


function doStep() {
  //super crazy code
  updateDiv.innerHTML = i;
  i++;
  if (i <= 100000) {
    timeout = setTimeout(doStep);
  }
}


doStep();
<div id="update"></div>
<button onclick="stopProcessing()">Stop</button>

如果您需要此过程尽可能快,您需要setTimeout。如果要与画布的动画缓冲区同步,请使用requestAnimationFrame

下面的代码显示setTimeout 的运行速度比requestAnimationFrame 快很多:

var rafEl = document.getElementById('requestAnimationFrame');
var stEl = document.getElementById('setTimeout');

var rafVal = 0;
var stVal = 0;

function rafFn() {
  rafVal++;
  rafEl.textContent = rafVal;
  if (rafVal < 1000) {
    requestAnimationFrame(rafFn);
  }
}

function stFn() {
  stVal++;
  stEl.textContent = stVal;
  if (stVal < 1000) {
    setTimeout(stFn);
  }
}

rafFn();
stFn();
    <div>requestAnimationFrame: <span id="requestAnimationFrame"></span></div>
    <div>setTimeout: <span id="setTimeout"></span></div>

【讨论】:

  • 在所有场景中使用 requestAnimationFrame 会大大改进使用递归 setTimeout 循环。
  • 上次我检查了requestAnimationFramesetTimeout 慢。它旨在在使用canvas 时提供稳定的动画帧。如果他不想在画布上绘图,那么setTimeout 可能是更好的选择。
  • requestAnimationFrame 通常与画布一起使用,因为对画布进行更改需要尽可能高的性能。但是,这并不意味着 requestAnimationFrame 仅用于画布。 requestAnimationFrame 将在页面重绘后运行,以提供无缝空间来处理和锁定视图。 setTimeout 当然可以工作,但是,它实际上应该只用于一次调用,而不是像这样的重复调用。
  • 使用 setTimeout 时,50 毫秒的步长将始终停留在页面重绘上,并在工作完成期间锁定 UI。
  • 我屈服了。我重新阅读了这个问题,并意识到他正在与canvas 合作,所以,requestAnimationFrame 是最好的选择。我还要说明,如果您只是在处理数据,甚至更改textContentinnerHTML,那么setTimeout 可能是更好的选择。
【解决方案3】:

要显示进度,您可以使用setInterval(),例如:

var interval = setInterval(function() {
  updateDiv.innerHTML = i;

  if (i == max)
    clearInterval(interval);

  i++;
}, 50);

代码:

var updateDiv = document.getElementById("update"); //Get a div to write progress to

var i = 0;
var max = 100;

var interval = setInterval(function() {
  updateDiv.innerHTML = i;

  if (i == max)
    clearInterval(interval);

  i++;
}, 50);
&lt;div id="update"&gt;&lt;/div&gt;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多