7

我有一个很长的 JavaScript 函数,它执行许多任务,我想通过在执行过程中使用消息更新 SPAN 元素的内容来向用户报告进度。我尝试在整个函数代码中添加 document.getElementById('spnProgress').innerText = ... 语句。

但是,当函数执行时,UI 不会更新,因此您只会看到写入 SPAN 的最后一条消息,这不是很有帮助。

我目前的解决方案是将任务分解为多个函数,在每个函数结束时,我设置 SPAN 消息,然后通过 window.setTimeout 调用以非常短的延迟(比如 10 毫秒)“触发”下一个函数。这会产生控制并允许浏览器在开始下一步之前使用更新的消息重新绘制 SPAN。

但是我发现这非常混乱且难以遵循代码,我认为必须有更好的方法。有没有人有什么建议?有什么方法可以强制 SPAN 重新绘制而不必离开函数的上下文?

谢谢

4

6 回答 6

7

你这样做的方式是正确的(目前,这可能会随着标准的出现和被采用而改变 [参见 Andrew Aylett 的回答],但暂时不会)。您必须像这样让浏览器进行 UI 更新。我发现我越是这样想,事情就越干净,但我最初的几次尝试确实很“混乱”。希望你能找到和你习惯一样的东西。

于 2010-04-07T12:28:30.130 回答
3

如果您已经控制了目标浏览器,则可以使用HTML5 工作线程在后台完成工作。

于 2010-04-07T12:33:05.403 回答
2

您需要注意,在某些浏览器中,如果您有一个长时间运行的脚本,您会收到一个脚本超时消息。所以实际上最好使用计时器来拆分它。

话虽如此,如果您正在寻找一种真正结构化的方法,那么您可以查看我为拼写检查项目编写的后台任务库。它允许您针对计时器上的数据数组实现 map/reduce。

https://github.com/jameswestgate/taskjs

于 2010-04-07T12:32:14.130 回答
0

如果您的函数的工作是在循环中执行的,那么这样的事情可能会起作用。它检查已经过去的时间量,如果 1/2 秒过去了,则更新进度条。(该示例未经测试。因此您可能需要稍微尝试一下。)

var start;
function longRunning(lastState){
    start = (new Date);
    for(var i = lastState; i < 1e6 /*= 1000000 iterations */; ++i){
         if((new Date)-start<500){
             // do your stuff;
         }
         else{
             // call a function to update the progress bar
             updateProgressBar();
             // continue the loop...
             setTimeout(function(){longRunning(i);},13);
             break;
         }
    }

}
longRunning(0);
于 2010-04-07T12:32:58.040 回答
0

这就像询问是否可以在不中断程序的情况下中断程序。答案是不。您必须使用 setTimeout() 或 setInterval() 将控制权传递给浏览器的渲染引擎。

如果您使用 setInterval(),您可以让该过程继续进行,并且在您执行的函数中只需更新一个外部变量,该变量将由 setInterval() 调用的函数轮询。这样,您只需拨打一个电话,而不是循环执行。

于 2010-04-07T12:28:36.887 回答
0

从来没听说过。您可以分解代码,使各个函数可以共享变量,因此:

var a = some_local_state();
runTasksWithProgress([
    function() {
        do_some_work(a);
        a = a + 1;
    },
    function() {
        do_some_other_work(a);
        a = a * 2;
    },
    ...
    ]);

runTasksWithProgress 有点棘手。您基本上会调用第一个任务,更新状态,然后设置回调以运行后续任务。

这种方法可能会减轻一些痛苦。

于 2010-04-07T12:31:00.480 回答