javascript 在每个循环中更新进度条
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/30987218/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Update progressbar in each loop
提问by reggie
I have a progress bar that I update in a loop of many iterations.
我有一个进度条,可以在多次迭代的循环中更新。
https://jsfiddle.net/k29qy0do/32/(open the console before you click the start button)
https://jsfiddle.net/k29qy0do/32/(点击开始按钮前打开控制台)
var progressbar = {};
$(function () {
progressbar = {
/** initial progress */
progress: 0,
/** maximum width of progressbar */
progress_max: 0,
/** The inner element of the progressbar (filled box). */
$progress_bar: $('#progressbar'),
/** Set the progressbar */
set: function (num) {
if (this.progress_max && num) {
this.progress = num / this.progress_max * 100;
console.log('percent: ' + this.progress + '% - ' + num + '/' + this.progress_max);
this.$progress_bar.width(String(this.progress) + '%');
}
},
fn_wrap: function (num) {
setTimeout(function() {
this.set(num);
}, 0);
}
};
});
$('#start_button').on('click', function () {
var iterations = 1000000000;
progressbar.progress_max = iterations;
var loop = function () {
for (var i = 1; i <= iterations; i++) {
if (iterations % i === 100) {
progressbar.set(i); //only updates the progressbar in the last iteration
//progressbar.fn_wrap(i); //even worse, since no output to the console is produced
}
}
}
//setTimeout(loop, 0);
loop();
});
The console is updated iteratively as expected. However, the progressbar is not updating.
控制台按预期迭代更新。但是,进度条没有更新。
The problem is that the browser window seems to 'hang' until the loop finishes. Only the console is updated, not the progressbar.
问题是浏览器窗口似乎“挂起”,直到循环完成。仅更新控制台,而不更新进度条。
I have tried to add the setTimeout, as suggested below, in several places. But that just makes things worse, because I then do not even get the console to output the progress while executing the loop.
我尝试在几个地方添加 setTimeout,如下所示。但这只会让事情变得更糟,因为我什至没有在执行循环时让控制台输出进度。
采纳答案by reggie
Okay, I found a solution in the answer to this question:
好的,我在这个问题的答案中找到了解决方案:
Javascript: How to update a progress bar in a 'for' loop
var i = 0;
(function loop() {
i++;
if (iterations % i === 100) {
progressbar.set(i); //updates the progressbar, even in loop
}
if (i < iterations) {
setTimeout(loop, 0);
}
})();
My solution: https://jsfiddle.net/ccvs4rer/3/
我的解决方案:https: //jsfiddle.net/ccvs4rer/3/
回答by Greg Burghardt
What you really want is an Asynchronous loop to allow the browser to update the DOM in between iterations.
您真正想要的是一个异步循环,以允许浏览器在迭代之间更新 DOM。
JSFiddle: http://jsfiddle.net/u5b6gr1w/
JSFiddle:http: //jsfiddle.net/u5b6gr1w/
function delayedLoop(collection, delay, callback, context) {
context = context || null;
var i = 0,
nextInteration = function() {
if (i === collection.length) {
return;
}
callback.call(context, collection[i], i);
i++;
setTimeout(nextInteration, delay);
};
nextInteration();
}
Some HTML:
一些 HTML:
<div class="progress-bar"><div style="width: 0"></div></div>
A splash of CSS:
CSS 飞溅:
.progress-bar {
border: 1px solid black;
background-color: #f0f0f0;
}
.progress-bar div {
background-color: red;
height: 1.25em;
}
And some JavaScript to wire things together:
还有一些 JavaScript 来将事物连接在一起:
var progressBar = document.querySelector(".progress-bar div"),
items = [1,2,3,4,5,6,7,8,9,10];
delayedLoop(items, 500, function(item, index) {
var width = (item / items.length * 100) + "%";
progressBar.style.width = width;
progressBar.innerHTML = width;
});
回答by Adam Boduch
回答by Lordbalmon
Lets break this down to steps
让我们将其分解为步骤
Step 1: Clean up HTML
第 1 步:清理 HTML
Assuming the purpose of your question is to understand how to work the progress bar and not the styles or the labels (loading, please be patient, etc.). Lets just have the progress bar and the start button.
假设您的问题的目的是了解如何使用进度条而不是样式或标签(加载,请耐心等)。让我们只有进度条和开始按钮。
<div id='progressbar-outer' style="">
<div id='progressbar' style=""></div>
</div>
<button id="start_button">Start</button>
Step 2: The Styles
第 2 步:样式
Lets make the progress bar visible to the user
让进度条对用户可见
#progressbar-outer {
height:2em;
border:5px solid #000;
width:15em;
}
#progressbar {
width:0%;
background-color:#F00;
height:100%;
}
Step 3: Using setTimeout
where it belongs
第 3 步:使用setTimeout
它所属的地方
In your code, you have used setTimeout
to set the value of your progress bar. However, the for
loop is still active.
在您的代码中,您曾经setTimeout
设置过进度条的值。但是,for
循环仍处于活动状态。
for (var i = 1; i <= iterations; i++) {
if (iterations % i === 100) {
progressbar.set(i); //only updates the progressbar in the last iteration
//progressbar.fn_wrap(i); //even worse, since no output to the console is produced
//setTimeout(function() {
// progressbar.set(i);
//}, 0);
}
}
The use of setTimeout
does not affect the rest of the code. Hence, the UI was held hostage till the loop ended. Try the following code.
的使用setTimeout
不会影响其余的代码。因此,在循环结束之前,用户界面一直被扣为人质。试试下面的代码。
$('#start_button').on('click', function () {
var iterations = 100;
progressbar.progress_max = iterations;
var loop = function (value) {
progressbar.set(value);
if (value < iterations) setTimeout(function () {
loop(value + 1)
}, 30);
else $('#progressbar').css('background-color', '#0F0');
}
loop(1);
});
Preview
预览
Try this fiddle: https://jsfiddle.net/Ljc3b6rn/4/
试试这个小提琴:https: //jsfiddle.net/Ljc3b6rn/4/
回答by klenium
What do you wnat to do? Why do you need it? You should only use a progressbar when you have to wait for something to finish. But we don't know what you do on your page.
你想做什么?你为什么需要它?当您必须等待某事完成时,您应该只使用进度条。但我们不知道你在你的页面上做了什么。
If you want to display the progress of an ajax upload:
$.ajax({ ... xhr: function() { var xhr = $.ajaxSettings.xhr(); $(xhr.upload).bind("progress", function(event) { var e = event.originalEvent; var percent = 0; if (e.lengthComputable) percent = Math.ceil(e.loaded/e.total*100); $("#progress").width(percent+"%"); }); return xhr; } ... });
For images, you need an ajax call:
$.ajax({ method: "GET", url: "http://example.com/path/image.jpg", xhr: function() {/* see the code above*/ } ... });
For getting the content of an uploaded file:
var reader = new FileReader(); reader.readAsText(uploadedFile); $(reader).bind("progress", function(e) { var percent = 0; if (e.lengthComputable) percent = Math.ceil(e.loaded/e.total*100); $("#progress").css("width", percent+"%"); });
For large around of process, like math or appending a lot of divs that will take 10+ secons:
Main.js:
var worker = new Worker("Worker.js"); $(worker).bind("message", function(data) { $("#progress").width((data*100)+"%"); });
Worker.js:
var total = 43483, finished = 0, doStuff = function() { ++finished; return 1+1; }; setInterval(function() { self.postMessage(finished/total); }, 100); for (var i = 0; i < total; ++i) setTimeout(doStuff, i*10);
Because it's nice, and you want to tell the user there's a progress when there isn't, just animate the div:
$("#progress").animate({width: "100%"}, 3000);
如果要显示 ajax 上传的进度:
$.ajax({ ... xhr: function() { var xhr = $.ajaxSettings.xhr(); $(xhr.upload).bind("progress", function(event) { var e = event.originalEvent; var percent = 0; if (e.lengthComputable) percent = Math.ceil(e.loaded/e.total*100); $("#progress").width(percent+"%"); }); return xhr; } ... });
对于图像,您需要一个 ajax 调用:
$.ajax({ method: "GET", url: "http://example.com/path/image.jpg", xhr: function() {/* see the code above*/ } ... });
获取上传文件的内容:
var reader = new FileReader(); reader.readAsText(uploadedFile); $(reader).bind("progress", function(e) { var percent = 0; if (e.lengthComputable) percent = Math.ceil(e.loaded/e.total*100); $("#progress").css("width", percent+"%"); });
对于大型流程,例如数学或附加大量需要 10 秒以上的 div:
主要.js:
var worker = new Worker("Worker.js"); $(worker).bind("message", function(data) { $("#progress").width((data*100)+"%"); });
工人.js:
var total = 43483, finished = 0, doStuff = function() { ++finished; return 1+1; }; setInterval(function() { self.postMessage(finished/total); }, 100); for (var i = 0; i < total; ++i) setTimeout(doStuff, i*10);
因为它很好,并且您想告诉用户有进度,而没有进度,只需为 div 设置动画:
$("#progress").animate({width: "100%"}, 3000);
回答by Trace
You can use promises
to wait until the width is set before continuing the loop.
Updating the progress bar for 1000000000 iterations will be slow if you go 1 by 1, so you might find it useful to decrease the update frequency.
Instead of a for
loop, I used a recursive function that loops when the promise has been fulfilled.
您可以使用promises
等到宽度设置好后再继续循环。
如果以 1 次 1 次的方式更新 1000000000 次迭代的进度条会很慢,因此您可能会发现降低更新频率很有用。
取而代之的是中for
环,我使用时的承诺已履行完毕即循环递归函数。
set: function (num) {
var deferred = $.Deferred();
if (this.progress_max && num) {
this.progress = num / this.progress_max * 100;
var self = this;
self.$progress_bar.animate({"width": String(this.progress) + '%'}, "fast", function() {
deferred.resolve();
});
return deferred;
}
}
$('#start_button').on('click', function () {
var iterations = 1000000000;
var i = 0;
progressbar.progress_max = iterations;
var loop = function(){
i+=100000000;
if(i <= iterations){
progressbar.set(i).then(function(){
loop();
}); ;
}
};
loop();
});
回答by Ori Drori
This are my 2 takes on the question:
这是我对这个问题的 2 个看法:
Using a web worker. The webworker blob code comes from here
Web worker code:
网络工作者代码:
<script type="text/ww">
function loop(e) {
var data = JSON.parse(e.data);
var i = parseInt(data.i, 10);
var iterations = parseInt(data.iterations, 10);
while (iterations % ++i !== 100 && i <= iterations);
if(i <= iterations) {
self.postMessage(JSON.stringify({ i: i, iterations: iterations }));
}
}
self.onmessage = function(e) {
loop(e);
};
</script>
The code:
代码:
var ww = document.querySelector('script[type="text/ww"]'),
code = ww.textContent,
blob = new Blob([code], {type: 'text/javascript'}),
blobUrl = URL.createObjectURL(blob),
worker = new Worker(blobUrl);
worker.onmessage = function(e) {
var data = JSON.parse(e.data);
var i = parseInt(data.i, 10);
var iterations = parseInt(data.iterations, 10);
progressbar.set(i);
worker.postMessage(JSON.stringify({ i: i, iterations: iterations }));
}
$('#start_button').on('click', function () {
var iterations = 1000000000;
progressbar.progress_max = iterations;
worker.postMessage(JSON.stringify({ i: 0, iterations: iterations }));
});
The other ideahangs the UI thread, but changes the width visually, as I use requestAnimationFrame to break the counting, change width of the progressbar, and then continue the count.
在其他的想法挂在UI线程,但在视觉上改变宽度,因为我用requestAnimationFrame打破计数,进度条的变化幅度,然后继续计数。
function loopFrame(i, iterations) {
requestAnimationFrame(function() {
if (iterations % i === 100) {
progressbar.set(i);
}
if(i < iterations) {
loopFrame(i + 1, iterations);
}
});
}
$('#start_button').on('click', function () {
var iterations = 1000000000;
console.log(iterations);
progressbar.progress_max = iterations;
loopFrame(0, iterations);
});
回答by amiuhle
You have to use window.requestAnimationFrame
, otherwise the browser will block until your loop is finished. The callback passed to requestAnimationFrame
will get a timestamp as a parameter which you might be able to use for calculations of the progress.
您必须使用window.requestAnimationFrame
,否则浏览器将阻塞,直到您的循环完成。传递给的回调requestAnimationFrame
将获得一个时间戳作为参数,您可以将其用于计算进度。
回答by Rex
Maybe this will be usefull.
也许这会很有用。
var service = new Object();
//function with interrupt for show progress of operations
service.progressWhile = new Object();
service.progressWhile.dTime = 50; //step ms between callback display function
service.progressWhile.i = 0; //index
service.progressWhile.timer = 0; //start time for cycle
//@parametr arr - array for actions
//@parametr actionCallback - The function for processing array's elements
//@parametr progressCallback - function to display the array index
function progressWhile(arr, actionCallback, progressCallback) {
try {
var d = new Date();
service.progressWhile.timer = d.getTime();
log(service.progressWhile.i);
if (service.progressWhile.i >= arr.length) {
service.progressWhile.i = 0;
return;
}
while (service.progressWhile.i < arr.length) {
actionCallback(arr[service.progressWhile.i++]);
d = new Date();
if (d.getTime() - service.progressWhile.timer > service.progressWhile.dTime) {
break;
}
}
if (progressCallback != undefined)
progressCallback(service.progressWhile.i);
} catch (er) {
log(er);
return;
}
setTimeout(function () {
progressWhile(arr, actionCallback, progressCallback);
}, 0);
}
回答by blue
Here's updated fiddle
这是更新的小提琴
I used animate to make it a progress bar like look and feel. Hope this will help you.
我使用 animate 使其成为类似于外观和感觉的进度条。希望这会帮助你。
var progressbar = {};
$(function() {
progressbar = {
/** initial progress */
progress : 0,
/** maximum width of progressbar */
progress_max : 0,
/** The inner element of the progressbar (filled box). */
$progress_bar : $('#progressbar'),
/** Method to set the progressbar.*/
set : function(num) {
if (this.progress_max && num) {
this.progress = num / this.progress_max * 100;
console.log('percent: ' + this.progress + '% - ' + num + '/' + this.progress_max);
$('#progressbar').animate({
width : String(this.progress) + '%',
}, 500, function() {
// Animation complete.
});
}
},
fn_wrap : function(num) {
setTimeout(function() {
this.set(num);
}, 0);
}
};
});
$('#start_button').on('click', function() {
$('#progressbar').css('width', '0%');
var iterations = 1000000000;
progressbar.progress_max = iterations;
var loop = function() {
for (var i = 1; i <= iterations; i++) {
if (iterations % i === 100) {
progressbar.set(i);
//only updates the progressbar in the last iteration
}
}
}
loop();
});
[1]: https://jsfiddle.net/k29qy0do/21/