javascript - 在循环多个变量时使用 setTimeout 更新进度条

标签 javascript jquery progress-bar settimeout

假设您有 3 个数组要循环,长度为 x、y 和 z,并且对于每个循环,您要更新一个进度条。例如:

function run() {
    x = 100;
    y = 100;
    z = 10;
    count = 0;
    for (i=0; i<x; i++) {
        //some code
        for (j=0; j<y; j++) {
            // some code
            for (k=0; k<z; k++) {
                //some code
                $("#progressbar").reportprogress(100*++count/(x*y*z));
            }
        }
    }
}

但是,在此示例中,进度条在函数完成之前不会更新。因此,我相信我需要使用 setTimeout 在函数运行时更新进度条,尽管我不确定在嵌套 for 循环时如何执行此操作。

我是否需要将每个循环分解成它自己的函数,或者我是否可以将它们保留为嵌套的 for 循环?

我创建了一个 jsfiddle 页面,以防您想运行当前函数:http://jsfiddle.net/jrenfree/6V4Xp/

谢谢!

最佳答案

TL;DR:使用 CPS:http://jsfiddle.net/christophercurrie/DHqeR/

accepted answer 中的代码有问题(截至 2012 年 6 月 26 日)是它创建了一个超时事件队列,这些事件在三重循环退出之前不会触发。您实际上并没有实时看到进度条更新,而是看到了一份关于变量值在内部闭包中被捕获时的延迟报告。

我希望您的“递归”解决方案看起来有点像使用 continuation-passing style以确保在您通过 setTimeout 放弃控制之前,您的循环不会继续。您可能不知道自己在使用 CPS,但如果您使用 setTimeout 来实现循环,您可能已经非常接近它了。

我详细说明了这种方法以供将来引用,因为知道它很有用,而且生成的演示比展示的演示效果更好。对于三重嵌套循环,它看起来有点复杂,因此对于您的用例来说可能有点过分,但在其他应用程序中可能很有用。

(function($){
    function run() {
        var x = 100,
            y = 100,
            z = 10,
            count = 0;

        /*
        This helper function implements a for loop using CPS. 'c' is
        the continuation that the loop runs after completion. Each
        'body' function must take a continuation parameter that it
        runs after doing its work; failure to run the continuation
        will prevent the loop from completing.
        */
        function foreach(init, max, body, c) {
            doLoop(init);
            function doLoop(i) {
                if (i < max) {
                    body(function(){doLoop(i+1);});
                }
                else {
                    c();
                }
            }
        }

        /*
        Note that each loop body has is own continuation parameter (named 'cx',
        'cy', and 'cz', for clarity). Each loop passes the continuation of the
        outer loop as the termination continuation for the inner loop.
        */
        foreach(0, x, function(cx) {
            foreach(0, y, function(cy) {
                foreach(0, z, function(cz) {
                    count += 1;
                    $('#progressbar').reportprogress((100*(count))/(x*y*z));
                    if (count * 100 % (x*y*z) === 0) {
                        /*
                        This is where the magic happens. It yields
                        control to the javascript event loop, which calls
                        the "next step of the foreach" continuation after
                        allowing UI updates. This is only done every 100
                        iterations because setTimeout can actually take a lot
                        longer than the specified 1 ms. Tune the iterations
                        for your specific use case.                   
                        */
                        setTimeout(cz, 1);
                    } else {
                        cz();
                    }
                }, cy);
            }, cx);
        }, function () {});    
    }

    $('#start').click(run);
})(jQuery);

你可以在jsFiddle上看到这个版本更新很顺利。

关于javascript - 在循环多个变量时使用 setTimeout 更新进度条,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7690465/

相关文章:

javascript - 如何让间隔定时器自动重新启动?

c# - 单击按钮读取动态表值

javascript - CKEDITOR.replace() 未定义

wpf - F# 异步和 WPF 进度条

Android ListView notifyDataSetChanged

javascript - 如何设置语义 UI 下拉菜单的宽度

javascript - 获取输入内的点击字符位置(不是坐标)

javascript - 如何使用 asp.net web 表单在 javascript 中使用 vb 变量?

javascript - 克隆元素并将其添加到另一个元素中

java - 如何显示具有模糊背景和禁用 UI 的进度条