How to report progress of a JavaScript function?
I have a JavaScript function which is quite long and performs a number of tasks, I would like to report progress to the user by updating the contents of a SPAN element with a message as I go. I tried adding document.getElementById('spnProgress').innerText = ... statements throughout the function code.
However, whilst the function is executing the UI will not update and so you only ever see the last message written to the SPAN which is not very helpful.
My current solution is to break the task up into a number of functions, at the end of each I set the SPAN message and then "trigger" the next one with a window.setTimeout call with a very short delay (say 10ms). This yields control and allows the browser to rep开发者_高级运维aint the SPAN with the updated message before starting the next step.
However I find this very messy and difficult to follow the code, I'm thinking there must be a better way. Does anyone have any suggestions? Is there any way to force the SPAN to repaint without having to leave the context of the function?
Thanks
The way you're doing it is the right way (at the moment, this may change as standards emerge and are adopted [see Andrew Aylett's answer], but not for a while yet). You have to yield like that to allow the browser to do its UI updates. I've found that the more I think like this, the cleaner things get, but my first few stabs at doing it were indeed quite "messy." Hopefully you find the same thing as you get used to it.
If you've got control of the target browser, you may be able to use an HTML5 worker thread to do the work in the background.
You need to be aware that in certain browsers you will receive a script timeout message if you have a long running script. So it is actually desirable to split this using a timer.
Having said this, if you are looking for a really structured way of doing this, then you can look at a background task library I wrote for a spell checking project. It allows you to implement map/reduce against arrays of data on a timer.
https://github.com/jameswestgate/taskjs
That's like asking whether it's possible to interrupt a procedure without interrupting a procedure. The answer is no. You have to use a setTimeout() or setInterval() to pass control to the browser's rendering engine.
If you use setInterval() you can get that process going and in your executing function simply update an external variable, which will be polled by the function called by setInterval(). That way you only have to make one call instead of doing them in a loop.
Not that I know of. You could break up your code in such a way that the individual functions can share variables, thus:
var a = some_local_state();
runTasksWithProgress([
function() {
do_some_work(a);
a = a + 1;
},
function() {
do_some_other_work(a);
a = a * 2;
},
...
]);
runTasksWithProgress is a bit tricky. You would basically invoke the first task, update the status, then set up a call-back to run subsequent tasks.
This approach might alleviate some of the pain.
Something like this may work if your function's work is performed in a loop. It checks the amount of time that has passed and updates the progress bar if 1/2 second has gone by. (The example is untested. So you may need to play with it a bit.)
var start;
function longRunning(lastState){
start = (new Date);
for(var i = lastState; i < 1e6 /*= 1000000 iterations */; ++i){
if((new Date)-start<500){
// do your stuff;
}
else{
// call a function to update the progress bar
updateProgressBar();
// continue the loop...
setTimeout(function(){longRunning(i);},13);
break;
}
}
}
longRunning(0);
精彩评论