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看到这个版本更新相当顺利。