TL; DR: использовать CPS: http://jsfiddle.net/christophercurrie/DHqeR/
Проблема с кодом в принятом ответе (по состоянию на 26 июня '12 г.) заключается в том, что он создает очередь событий тайм-аута, которые не срабатывают до тех пор, пока тройной цикл уже не завершится. Вы на самом деле не видите обновления индикатора выполнения в режиме реального времени, но видите поздний отчет о том, какими были значения переменных во время их захвата во внутреннем закрытии.
Я бы ожидал, что ваше «рекурсивное» решение похоже на использование стиля передачи продолжения , чтобы гарантировать, что ваш цикл не продолжится до тех пор, пока вы не передадите управление через 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 , что эта версия обновляется довольно гладко.