在循环多个变量时使用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循环时这样做。

我是否需要将每个循环分解为自己的函数,还是可以将它们作为循环嵌套?

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

谢谢!

如果要使用setTimeout,可以将x,y,z和count变量捕获到闭包中:

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

现场演示 。

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

接受的答案中的代码问题 (截至12月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上看到此版本更新非常顺利。

reportprogress插件中的jquery函数可能使用setTimeout。 例如,如果您使用setTimeout并使其在0毫秒后运行,则并不意味着它将立即运行。 当没有执行其他JavaScript时,将执行该脚本。

在这里你可以看到我尝试在等于0时尝试记录计数。如果我在setTimeout回调函数中执行它然后在所有循环之后执行,你将得到100000没有0.这解释了为什么进度条只显示100%。 js小提琴链接到这个脚本

 function run() { x = 100; y = 100; z = 10; count = 0; for (i=0; i 

包装后的所有内容(i)在setTimeout回调函数中使进度条工作。 js小提琴链接

编辑:刚检查项目的样式设置代码是否一直在执行。 我认为首先执行javascript然后显示CSS更改可能是浏览器优先级。

我写了另一个例子,我用setInterval函数替换了第一个for循环。 像这样使用它有点不对,但也许你可以用这个黑客来解决这个问题。

 var i=0; var interval_i = setInterval(function (){ for (j=0; j 

JS小提琴

我找到了一个基于最后一个回复的解决方案,但是将间隔时间改为一个。 此解决方案显示加载程序,而主线程正在执行密集任务。

定义此function:

  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); });