What I'm trying to do is to update a simple div to say "Processing..." before executing a CPU-intensive script (it takes 3-12 seconds to run, no AJAX) then update the div to say "Finished!" when done.
What I'm seeing is the div never gets updated with "Processing...". If I set a breakpoint immediately after that command, then the div text does get updated, so I know the syntax is correct. Same behavior in IE9, FF6, Chrome13.
Even when bypassing jQuery and using basic raw Javascript, I see the same issue.
You'd think this would have an easy answer. However, since the jQuery .html() and .text() don't have a callback hook, that's not an option. It's also not animated, so there is no .queue to manipulate.
You can test this yourselves using the sample code I prepared below that shows both the jQuery and Javascript implementations with a 5 second high-CPU function. The code is easy to follow. When you click either the button or the link, you never see "Processing..."
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js" ></script>
<script type="text/javascript">
function addSecs(d, s) {return new Date(d.valueOf()+s*1000);}
function doRun() {
document.getElementById('msg').innerHTML = 'Processing JS...';
start = new Date();
end = addSecs(start,5);
do {start = new Date();} while (end-start > 0);
document.getElementById('msg').innerHTML = 'Finished JS';
}
$(function() {
$('button').click(function(){
$('div').text('Processing JQ...');
start = new Date();
end = addSecs(start,5);
do {start = new Date();} while (end-start > 0);
$('div').text('Finished JQ');
});
});
</script>
</head>
<body>
<div id="msg">Not Started</div>
<button>jQuery</button>
<a href="#" onclick="doRun()">javascript</a>
</body>
</html>
set it to processing, then do a setTimeout to prevent the cpu intensive task from running until after the div has been updated.
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js" ></script>
<script>
function addSecs(d, s) {return new Date(d.valueOf()+s*1000);}
function doRun() {
document.getElementById('msg').innerHTML = 'Processing JS...';
setTimeout(function(){
start = new Date();
end = addSecs(start,5);
do {start = new Date();} while (end-start > 0);
document.getElementById('msg').innerHTML = 'Finished Processing';
},10);
}
$(function() {
$('button').click(doRun);
});
</script>
</head>
<body>
<div id="msg">Not Started</div>
<button>jQuery</button>
<a href="#" onclick="doRun()">javascript</a>
</body>
</html>
you can modify the setTimeout delay as needed, it may need to be larger for slower machines/browsers.
Edit:
You could also use an alert or a confirm dialog to allow the page time to update.
document.getElementById('msg').innerHTML = 'Processing JS...';
if ( confirm( "This task may take several seconds. Do you wish to continue?" ) ) {
// run code here
}
You have a loop that runs for 5 seconds and freezes the web browser during that time. Since the web browser is frozen it can't do any rendering. You should be using setTimeout()
instead of a loop, but I'm assuming that loop is just a replacement for a CPU intensive function that takes a while? You can use setTimeout to give the browser a chance to render before executing your function:
jQuery:
$(function() {
$('button').click(function(){
(function(cont){
$('div').text('Processing JQ...');
start = new Date();
end = addSecs(start,5);
setTimeout(cont, 1);
})(function(){
do {start = new Date();} while (end-start > 0);
$('div').text('Finished JQ');
})
});
});
Vanilla JS:
document.getElementsByTagName('a')[0].onclick = function(){
doRun(function(){
do {start = new Date();} while (end-start > 0);
document.getElementById('msg').innerHTML = 'Finished JS';
});
return false;
};
function doRun(cont){
document.getElementById('msg').innerHTML = 'Processing JS...';
start = new Date();
end = addSecs(start,5);
setTimeout(cont, 1);
}
You should also remember to always declare all variables using the var keyword, and avoid exposing them to the global scope. Here is a JSFiddle:
http://jsfiddle.net/Paulpro/ypQ6m/
I had to wait for jQuery to manipulate the DOM and then grap the changes (load form fields multiple times into a form, then to submit it). The DOM grew and the insertion took longer and longer. I saved the changes using ajax to have to user to be able to continue where he left off.
This did NOT WORK as intended:
jQuery('.parentEl').prepend('<p>newContent</p>');
doSave(); // wrapping it into timeout was to short as well sometimes
Since jQuery functions like .prepend()
do continue the chain only when done, the following seemed to do the trick:
jQuery('.parentEl').prepend('<p>newContent</p>').queue(function() {
doSave();
});
As of 2019 one uses double requesAnimationFrame
to skip a frame instead of creating a race condition using setTimeout.
....
function doRun() {
document.getElementById('msg').innerHTML = 'Processing JS...';
requestAnimationFrame(() =>
requestAnimationFrame(function(){
start = new Date();
end = addSecs(start,5);
do {start = new Date();} while (end-start > 0);
document.getElementById('msg').innerHTML = 'Finished Processing';
}))
}
...