更新网页以显示javascript在循环中运行时的进度

时间:2015-08-30 17:30:34

标签: javascript html settimeout

我编写的javascript需要20-30秒才能处理,我想通过更新网页上的进度条来显示进度。

我尝试使用setTimeout来重新绘制网页。

这就是我的代码的样子:

function lengthyFun(...){
   for(...){
      var progress = ...
      document.getElementById('progress-bar').setAttribute('style',"width:{0}%".format(Math.ceil(progress)));

      var x = ...

      // Processing
      setTimeout(function(x) { return function() { ...  }; }(x), 0);
   }
}

它不起作用,我知道它为什么不起作用,但我不知道如何重构我的代码以使其工作。

3 个答案:

答案 0 :(得分:1)

正如您可能知道的,这里的问题是您的主进程(需要花费大量时间的进程)阻止任何渲染。那是因为JavaScript(主要)是单线程的。

从我的角度来看,你有两个解决方案可以做到这一点。 第一个是将主要过程分解为不同的部分,并在每个部分之间进行渲染。即你可以有类似的东西(使用Promises):

var processParts = [/* array of func returning promises */];

function start(){
    // call the first process parts
    var firstPartPromise = (processParts.shift())();
    // chain it with all the other process parts interspersed by updateDisplay
    return processParts.reduce(function(prev, current){
        return val.then(current).then(updateDisplay);
    }, firstPartPromise);
}

您可能需要为承诺(one here)填充。如果你使用jQuery,他们有一个(非标准的)非常糟糕的实现。

第二个解决方案可以是使用webworkers,它允许您在JavaScript中创建线程。它适用于所有现代浏览器。 它可能是您的最佳解决方案。 我从来没有使用它们,但你应该能够做到这样的事情:

var process = new Worker("process.js");

worker.onmessage(function(event){
    updateProgress(event.data.progress)
});

in process.js

postMessage({progress: 0.1});
// stuff
postMessage({progress: 0.4});
// stuff
postMessage({progress: 0.7});
//etc

答案 1 :(得分:0)

尝试将progress元素属性min设置为0,将max设置为20000,将value设置为0;如果value小于max增加value 1000,则创建函数;利用持续时间设置为setTimeout的{​​{1}}来递归调用函数,直到1000到达value

max
var p = document.querySelector("progress");

function redraw() {
   if (p.value < p.max) {
     p.value += 1000;
     setTimeout("redraw()", 1000)
   }
}

redraw()

答案 2 :(得分:0)

我知道有几种方法可以通过Javascript触发顺序HTML重绘:

  • 增量超时期限
  • 递归方法调用

执行此操作的第一种也是最简单的方法是在循环中的超时间隔使用乘数(例如迭代器)。如果操作独立于外部变量并且仅需要以有限且相对较少的次数运行,则该方法应该是足够的。需要/可能发生的操作越多,资源的压力就越大 - 仅用于计算间隔。当处理时间超过超时间隔时,另一个缺点会起作用,导致观察到的重绘间隔的连锁。结果可能是网页完全冻结,直到所有操作完成。

实施例

for (var i=0, limit=n; i<limit; i++) {
    setTimeout((function(params) {
        return  function() {
            some_func(params);
        }
    })(param_values), i*1000);
}

第二种方法有点复杂,但无论超时间隔如何,都可以保证每次操作之间的重绘。这里,超时仅影响重绘之间的时间,并且抵抗连续操作变量的影响。但是,当前操作的处理时间仍然是观察间隔的一个因素,如果操作是计算密集的话,仍然会在重绘之间冻结网页。

实施例

var limit = n;
var i = 0;
recursive_timeout();
function recursive_timeout() {
    setTimeout((function(params) {
        return function() {
            some_func(params);
            i++;
            if (i<limit) {
                recursive_timeout();
            }
        }
    })(param_values, i, limit), 1000);
}

精炼示例(基于guest271314的回答)

var still_true = true;
recursive_timeout();
function recursive_timeout() {
    some_func(params);
    if (still_true) {
        setTimeout(function() {recursive_timeout();}, 1000);
    }
}

虽然增量方法适用于简单任务,但递归可以可靠地执行重绘。如果每个操作的处理时间长是一个问题,那么除了使用递归之外,可能值得深入研究异步任务,以避免使网页暂时无法使用。

无论如何,希望这有帮助!

哈!刚刚实现了guest271314提出了一个更加优雅的递归方法的例子......哦,更多的信息不会伤害。