在每个循环中更新进度条
Update progressbar in each loop
我有一个进度条,我在多次迭代的循环中更新它。
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();
});
控制台按预期迭代更新。
但是,进度条没有更新。
问题是浏览器 window 似乎 'hang' 直到循环结束。
只更新控制台,不更新进度条。
我已经尝试按照下面的建议在几个地方添加 setTimeout。
但这只会让事情变得更糟,因为我什至没有让控制台在执行循环时输出进度。
我猜你的所有进度更新都在同一个调用堆栈中 运行。 JavaScript 代码为 运行 时,DOM 无法更新。也许这个 question 可以帮助您找到解决方法。
这里更新了fiddle
我用动画让它看起来像一个进度条。
希望对您有所帮助。
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/
好的,我在这个问题的答案中找到了解决方案:
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);
}
})();
我的解决方法:
https://jsfiddle.net/ccvs4rer/3/
您真正想要的是一个异步循环,允许浏览器在迭代之间更新 DOM。
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();
}
一些HTML:
<div class="progress-bar"><div style="width: 0"></div></div>
飞溅 CSS:
.progress-bar {
border: 1px solid black;
background-color: #f0f0f0;
}
.progress-bar div {
background-color: red;
height: 1.25em;
}
还有一些 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;
});
您可以使用promises
等到设置宽度后再继续循环。
如果逐一更新 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();
});
让我们将其分解为步骤
第 1 步:清理 HTML
假设您的问题的目的是了解进度条的工作原理,而不是样式或标签(正在加载,请耐心等待等)。让我们只有进度条和开始按钮。
<div id='progressbar-outer' style="">
<div id='progressbar' style=""></div>
</div>
<button id="start_button">Start</button>
第 2 步:样式
让用户可以看到进度条
#progressbar-outer {
height:2em;
border:5px solid #000;
width:15em;
}
#progressbar {
width:0%;
background-color:#F00;
height:100%;
}
第 3 步:在属于它的地方使用 setTimeout
在您的代码中,您已使用 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);
}
}
使用setTimeout
不影响其余代码。因此,UI 被扣为人质,直到循环结束。试试下面的代码。
$('#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);
});
预览
试试这个 fiddle:https://jsfiddle.net/Ljc3b6rn/4/
您必须使用 window.requestAnimationFrame
,否则浏览器将阻塞,直到您的循环结束。传递给 requestAnimationFrame
的回调将获得一个时间戳作为参数,您可以将其用于计算进度。
你想做什么?你为什么需要它?当你必须等待某事完成时,你应该只使用进度条。但是我们不知道你在你的页面上做了什么。
如果要显示 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 秒以上的 divs:
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);
因为它很好,而且你想告诉用户有进展而没有进展,只需为 div:
设置动画
$("#progress").animate({width: "100%"}, 3000);
这是我对这个问题的 2 个看法:
使用 web worker. The webworker blob code comes from here
网络工作者代码:
<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>
代码:
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 }));
});
other idea 挂起 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);
});
也许这会有用。
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);
}
我有一个进度条,我在多次迭代的循环中更新它。
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();
});
控制台按预期迭代更新。 但是,进度条没有更新。
问题是浏览器 window 似乎 'hang' 直到循环结束。 只更新控制台,不更新进度条。
我已经尝试按照下面的建议在几个地方添加 setTimeout。 但这只会让事情变得更糟,因为我什至没有让控制台在执行循环时输出进度。
我猜你的所有进度更新都在同一个调用堆栈中 运行。 JavaScript 代码为 运行 时,DOM 无法更新。也许这个 question 可以帮助您找到解决方法。
这里更新了fiddle
我用动画让它看起来像一个进度条。 希望对您有所帮助。
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/
好的,我在这个问题的答案中找到了解决方案:
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);
}
})();
我的解决方法: https://jsfiddle.net/ccvs4rer/3/
您真正想要的是一个异步循环,允许浏览器在迭代之间更新 DOM。
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();
}
一些HTML:
<div class="progress-bar"><div style="width: 0"></div></div>
飞溅 CSS:
.progress-bar {
border: 1px solid black;
background-color: #f0f0f0;
}
.progress-bar div {
background-color: red;
height: 1.25em;
}
还有一些 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;
});
您可以使用promises
等到设置宽度后再继续循环。
如果逐一更新 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();
});
让我们将其分解为步骤
第 1 步:清理 HTML
假设您的问题的目的是了解进度条的工作原理,而不是样式或标签(正在加载,请耐心等待等)。让我们只有进度条和开始按钮。
<div id='progressbar-outer' style="">
<div id='progressbar' style=""></div>
</div>
<button id="start_button">Start</button>
第 2 步:样式
让用户可以看到进度条
#progressbar-outer {
height:2em;
border:5px solid #000;
width:15em;
}
#progressbar {
width:0%;
background-color:#F00;
height:100%;
}
第 3 步:在属于它的地方使用 setTimeout
在您的代码中,您已使用 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);
}
}
使用setTimeout
不影响其余代码。因此,UI 被扣为人质,直到循环结束。试试下面的代码。
$('#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);
});
预览
试试这个 fiddle:https://jsfiddle.net/Ljc3b6rn/4/
您必须使用 window.requestAnimationFrame
,否则浏览器将阻塞,直到您的循环结束。传递给 requestAnimationFrame
的回调将获得一个时间戳作为参数,您可以将其用于计算进度。
你想做什么?你为什么需要它?当你必须等待某事完成时,你应该只使用进度条。但是我们不知道你在你的页面上做了什么。
如果要显示 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 秒以上的 divs:
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);
因为它很好,而且你想告诉用户有进展而没有进展,只需为 div:
设置动画$("#progress").animate({width: "100%"}, 3000);
这是我对这个问题的 2 个看法:
使用 web worker. The webworker blob code comes from here
网络工作者代码:
<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>
代码:
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 }));
});
other idea 挂起 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);
});
也许这会有用。
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);
}