I have a loop which needs to be run 200 million times in a browser. It's a simulator which several people need to use regularly. It takes about 15 minutes to run, but during this time, the browsers will frequently pop up a warning with "this script is taking too long" etc., and it completely hangs Firefox during the function. This also means the page does not update my status indicator (which is just a number).
I have googled "javascript yield" and read the first 4 pages of hits. Some discuss a new "yield" keyword, but there is only one description and example, which I find incomprehensible, e.g. "The function containing the yield keyword is a generator. When you call it, it's formal parameters are bound to actual arguments, but it's body isn't actually evaluated". Does yield
yield to the UI?
One of the few solutions I did find is this old post which uses the deprecated callee argument and a timer to call itself: http://www.julienlecomte.net/blog/2007/10/28/
However, the above example doesn't contain any loop variables or state, and when I add these it falls apart, and my net result is always zero.
It also doesn't do chunking, but I have found some other examples which chunk using "index % 100 == 0" on every iteration. However, this seems to be a slow way of doing it. E.g. this:
How to stop intense Javascript loop from freezing the browser
But it doesn't have any way to update progress, and doesn't yield to the UI (so still hangs the browser). Here is a test version which hangs the browser during execution:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script>
var spins = 1000000
var chunkSize = 1000;
var chunk;
function Stats() {this.a=0};
var stats = new Stats();
var big;
var index = 0;
var process = function() {
for (; index < spins; index++) {
stats.a++;
big = (big/3.6)+ big * 1.3 * big / 2.1;
console.write(big);
// Perform xml processing
if (index + 1 < spins && index % 100 == 0) {
document.getElementById("result").innerHTML = stats.a;
setTimeout(process, 5);
}
}
document.getElementById("result").innerHTML = stats.a;
};
</script>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Untitled Document</title>
</head>
<body onload="process()">
<div id=result>result goes here.</div>
</body>
</html>
and here is another attempt which the stats.a
is always zero (So I presume there is some scoping issue):
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script>
var spins = 1000000
var chunkSize = 1000;
var chunk;
function Stats() {this.a=0};
var stats = new Stats();
function doIt() {
function spin() {
for (spinIx=0; (spinIx<chunkSize) && (spinIx+chunk < spins); spinIx++) {
stats.a++;
}
}
for (chunk =0; chunk < spins; chunk+=chunkSize){
setTimeout(spin, 5);
document.getElementById("result").innerHTML = stats.a;
}
document.getElementById("result").innerHTML = stats.a;
}
</script>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Untitled Document</title>
</head>
<body onload="doIt()">
<div id=result>result goes here.</div>
</body>
</html>
I've spent 48 hours trying to get this working - either I am very dumb or this is very hard. Any ideas?
Several people have suggested web workers. I tried several days to get his working, but I could not find a similar example which passes a number etc. The code below was my last attempt to get it working, but the result is always 0 when it should be 100000. I.e. it fails in the same way that my second example above fails.
spinMaster.html:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
</head>
<body>
<script>
if(typeof(Worker)==="undefined") {
document.write("<h1>sorry, your browser doesnt support web workers, please use firefox, opera, chorme or safari</h1>");
}
var worker =new Worker("spinWorker.js");
worker.postMessage({times:1000000});
worker.onmessage=function(event){
document.getElementById("result").innerHTML=event.data;
};
</script>
<div id="result">result goes here</div>
</body>
</html>
spinWorker.js
function State() {
this.a=0;
}
var state = new State();
self.addEventListener('message', spin, false);
function spin(e) {
var times, i;
times = e.data.times;
//times = 1000000; // this doesnt work either.
for(i;i<times;i++) {
state.a++;
}
self.postMessage(state.a);
}
resultant output: 0