Reputation: 1440
I'm working on a way that I can keep multiple instances of a php program updated to the same version without much effort.
My software checks for an RSS feed, which includes information on the most up-to-date version. If it sees that there is an update available, it shows a link to the "Update" page. When the user clicks on the Update link, I have a jquery function that updates each file that needs to be updated one at a time:
$.getJSON('http://softwareserver.com/updateFileList.php?fromver=1.0', function(data) {
var filesToDo = data.length;
$.each(data, function(num, file) {
num = num + 1;
action = file.charAt(0);
name = file.substring(1);
if ( action == "+" )
{
$('#upsole').append("<p><strong>Adding</strong> " + name + ". . .</p>");
$.get("{$var['sbpurl']}/updateAction.php", { action: "add", file: name, revision: "{$TMP['rev']}" } );
}
else if ( action == "-" )
{
$('#upsole').append("<p><strong>Removing</strong> " + name + ". . .</p>");
$.get("{$var['sbpurl']}/updateAction.php", { action: "remove", file: name, revision: "{$TMP['rev']}" } );
}
else if ( action == "~" )
{
$('#upsole').append("<p><strong>Updating</strong> " + name + ". . .</p>");
$.get("{$var['sbpurl']}/updateAction.php", { action: "update", file: name, revision: "{$TMP['rev']}", version: "{$TMP['ver']}" } );
}
var completed = num / filesToDo * 100;
$('#barcolor').css('width', completed + '%');
$('#numProgress').html(completed.toFixed(0));
if ( completed == 100 )
{
$('#conutt').html('<a href="index.php" class="button"> Continue > </a>');
$('#upsole').append("<p><strong>Update Complete</strong></p>");
}
});
});
I show each file name in a div container as it's being updated; I also have a progress bar that goes from 0-100% as each file is updated until all files have been updated.. but it simply goes from 0 to 100 when it's all finished. It doesn't actually show the progress as each file updates in between like I want it to.
I expect my progress bar to go from 0%, to like 7%, 15%, etc, until it reaches 100% but nothing happens until it's all finished.. so as I said, it just goes from 0% to 100%.
Is $.each not the best solution for this?
Upvotes: 0
Views: 523
Reputation: 18078
c0nfus3d1, I see you have an answer from @mu but maybe you would like to see a solution I started to prepare earlier before a major interruption.
As far as I can see .each()
is good for the job and your code needs only very light modification to make it work.
However, it could also benefit from further mods to give something like this tidier, more readable version:
$.getJSON('http://softwareserver.com/updateFileList.php?fromver=1.0', function(data) {
var filesToDo = data.length;
var filesDone = 0;
var activityStr = "<p><strong>%s1</strong>%s2</p>";//activity message template
function showProgress() {
filesDone++;
var completed = 100 * filesDone / filesToDo;
$('#barcolor').css('width', completed + '%');
$('#numProgress').html(completed.toFixed(0));
if ( filesDone == filesToDo ) {
$('#conutt').html('<a href="index.php" class="button"> Continue > </a>');
$('#upsole').append(activityStr.replace('%s1', 'Update Complete').replace('%s2', ''));
}
}
$.each(data, function(num, file) {
var dataObj = {//data common to all cases below.
file: file.substring(1),
revision: "{$TMP['rev']}"
},
a, d;
switch (file.charAt(0)) {
case "+":
a = 'Adding';
d = $.extend({action:"add"}, dataObj);
break;
case "-":
a = "Removing";
d = $.extend({action:"remove"}, dataObj);
break;
case "~":
a = "Updating";
d = $.extend({action:"update", version:"{$TMP['ver']}"}, dataObj);
}
$('#upsole').append(activityStr.replace('%s1', a).replace('%s2', ' ' + d.file + '. . .'));
$.get("{$var['sbpurl']}/updateAction.php", d).done(showProgress);
});
});
untested
Notes :
$.extend()
is used to merge general and case-specific objects, to build the inner $.get
data..replace()
to a string template.showProgress()
is called in response to completion of each of the inner .get()
calls..get()
requests are issued in very quick succession, the responses are handled as and when - and in whatever order - they arrive back from the server.Maybe this includes some ideas you can use.
Upvotes: 1
Reputation: 434695
No, $.each
isn't quite the right tool for this job. Your main problem is that JavaScript in the browser is single threaded so all your DOM updates:
$('#upsole').append(...)
$('#barcolor').css(...)
$('#numProgress').html(...)
will just be queued up to be handled when the browser gets control back. So when does the browser regain control? After the $.each
is finished of course. You see the result: the user sees things go from nothing to finished in one step. You also have the asynchronous nature of $.get
to consider.
You need to hand control back to the browser after each DOM update. You have a bunch of $.get
calls so you could use their success callbacks to interleave your DOM changes with the browser's response to those changes. Something like this:
$.getJSON('http://softwareserver.com/updateFileList.php?fromver=1.0', function(data) {
var filesToDo = data.length;
var next_one = function() {
var file = data.shift();
if(!file) {
// If `data` is empty then we're all done so we can put the UI into
// the "update complete" state and end the process by not calling
// next_one() again.
$('#conutt').html('<a href="index.php" class="button"> Continue > </a>');
$('#upsole').append("<p><strong>Update Complete</strong></p>");
return;
}
// Set up the next iteration by parsing `file`. We put the `$.get` stuff
// into an object so that we only have one `$.get` call; the reason for
// this will be clear shortly.
var action = file.charAt(0);
var name = file.substring(1);
var get = { };
if(action == '+') {
get = {
msg: '<p><strong>Adding</strong> ' + name + '...</p>',
url: "{$var['sbpurl']}/updateAction.php",
params: { action: "add", file: name, revision: "{$TMP['rev']}" }
};
}
else if(action == '-') {
//...
}
else if(action == '~') {
//...
}
// Tell them what we're about to do...
$('#upsole').append(get.msg);
// This `$.get` call is asynchronous so the browser will get control here
// and any pending DOM updates will be applied.
$.get(get.url, get.params, function() {
// Update the DOM with the latest status.
var completed = (filesToDo - data.length) / filesToDo * 100;
$('#barcolor').css('width', completed + '%');
$('#numProgress').html(completed.toFixed(0));
// And trigger the next iteration.
next_one();
});
};
// Crank it up.
next_one();
});
The basic idea is to iterate over data
until it is empty, each iteration removes the first element of data
so we can detect the end of data
(and the end of our iterating) by seeing when it returns undefined
(which is falsey). Each iteration will trigger a $.get
, that will return control to the browser to allow the DOM to be updated. The $.get
callback will then trigger the next iteration.
Niceties such as error handling is left as an exercise for the reader.
Upvotes: 1