当循环多个变量时,使用setTimeout更新进度条
Using setTimeout to update progress bar when looping over multiple variables
假设您有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/
可接受答案中的代码的问题(截至2012年6月26日)是,它创建了一个超时事件队列,直到三重循环已经退出才触发。实际上,您并没有看到进度条实时更新,而是看到了在内部闭包中捕获变量时变量值的最新报告。
我希望你的"递归"解决方案看起来有点像使用延续传递风格,以确保你的循环不会继续,直到你通过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上看到这个版本的更新非常顺利。
如果你想使用setTimeout,你可以将x, y, z和count变量捕获到闭包中:
function run() {
var x = 100,
y = 100,
z = 10,
count = 0;
for (var i=0; i<x; i++) {
for (var j=0; j<y; j++) {
for (var k=0; k<z; k++) {
(function(x, y, z, count) {
window.setTimeout(function() {
$('#progressbar').reportprogress((100*count)/(x*y*z));
}, 100);
})(x, y, z, ++count);
}
}
}
}
现场演示。
可能是reportprogress插件中的jquery函数使用了setTimeout。例如,如果您使用setTimeout并使其在0毫秒后运行,这并不意味着它将立即运行。该脚本将在不执行其他javascript时执行。
在这里你可以看到,我尝试记录计数,当它等于0。如果我在setTimeout回调函数中这样做,那么在所有周期后执行,你将得到100000没有0。这就解释了为什么进度条只显示100%。链接到这个脚本
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
if(count===0) {
console.log('log emidiatelly ' + count);
setTimeout(function(){
console.log('log delayed ' + count);
},0);
}
count++;
}
}
}
}
console.log('started');
run();
console.log('finished');
在setTimeout回调函数中包装for(i)之后的所有内容使进度条工作。js链接
编辑:刚刚检查了item的样式设置代码实际上一直在执行。我认为这可能是一个浏览器优先执行javascript,然后显示CSS的变化。
我写了另一个例子,其中我用setInterval函数替换了第一个for循环。这样使用它有点错误,但也许你可以用这个hack来解决这个问题。
var i=0;
var interval_i = setInterval(function (){
for (j=0; j<y; j++) {
for (k=0; k<z; k++) {
$("#progressbar").reportprogress(100*++count/(x*y*z));
}
}
i++;
if((i<x)===false) {
clearInterval(interval_i);
}
},0);
JS小提琴
我找到了一个基于最后一个回复的解决方案,但将间隔时间更改为1。这个解决方案显示了一个加载器,而主线程正在执行一个密集的任务。
定义这个函数:
loading = function( runme ) {
$('div.loader').show();
var interval = window.setInterval( function() {
runme.call();
$('div.loader').hide();
window.clearInterval(interval);
}, 1 );
};
然后这样命名:
loading( function() {
// This take long time...
data.sortColsByLabel(!data.cols.sort.asc);
data.paint(obj);
});
- Javascript循环不会自我更新
- setInteval vs setTimeout
- 添加文字和评论功能更新Div
- AngularJS:ng之后,重复$scope值未按预期更新
- 如何通过数组更新角度子范围
- Angularjs 更新 setTimeout 中的范围变量不起作用
- Rails和jQuery-尝试使用setTimeout主动更新数据
- Angular2,视图在settimeout变量更改后未更新
- 使用setTimeout节点jadeexpress动态更新内容
- 使用setTimeout()angularjs可以缓慢更新UI
- Flux/Alt-setTimeout未更新存储
- 如何正确地让浏览器在使用setTimeout的循环中间进行更新
- 为什么不't Settimeout提取更新的图像URL
- 从Javascript中的setTimeout函数中更新对象的属性值
- 在setTimeout问题中更新JSON对象
- 当循环多个变量时,使用setTimeout更新进度条
- 更新DIV并在1秒后使用setTimeout再次清空它
- 在MVC3中更新局部视图的SetTimeout是随机执行的
- 在阻塞代码之前通过setTimeout或promise更新DOM
- 在更新循环中使用setTimeout vs比较时间戳