Prevent browser blocking of JavaScript by a large loop

I have a loop that needs to be run 200 million times in the browser. This is a simulator that several people should use regularly. It takes about 15 minutes to start, but during this time, browsers often come out with a warning that β€œthis script takes too long”, etc., and it completely freezes Firefox during the function. It also means that the page is not updating my status indicator (this is just a number).

I have a google exit for javascript and read the first 4 pages of hits. Some discuss the new keyword "yield", but there is only one description and an example that I find obscure, for example. "The function containing the yield keyword is a generator. When you call it, the formal parameters are bound to the actual arguments, but its body is not actually evaluated." Is yield suitable for the user interface?

One of the few solutions I found is an old post that uses an outdated caller argument and a timer to call itself: http://www.julienlecomte.net/blog/2007/10/28/

However, the above example does not contain any loop or state variables, and when I add them, it falls apart and my net result is always zero.

It also does not perform chunking, but I found several other examples that use "index% 100 == 0" at each iteration. However, this seems to be a slow way to do this. For example. this is:

How to stop javascript intensive loop from freezing browser

But it has no way to update progress and is not inferior to the user interface (so the browser still hangs). Here is a test version that freezes the browser at runtime:

 <!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 try, which stats.a always zero (so I assume there are some problems with defining the scope):

 <!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 spent 48 hours trying to get this to work - either I'm very dumb or it's very complicated. Any ideas?

Several people have suggested web workers. I tried a few days to get it working, but I could not find a similar example that passes a number, etc. Below is my last attempt to get it to work, but the result is always 0 when it should be 100000. Ie it fails just like my second example above.

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); } 

total result: 0

+7
source share
5 answers

Web workers sound like the best solution.

I wrote it quickly, so I don't know if it works. Performance will be very bad ...

Edit: Changed in the same format as the poster. Tested

 <!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 = 0.0; var index = 0; function workLoop() { index += 1; stats.a++; big = (big/3.6)+ big * 1.3 * big / 2.1; console.log(big); // Perform xml processing document.getElementById('result').innerHTML = stats.a; if (index < spins) { setTimeout(workLoop, 5); } } </script> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Untitled Document</title> </head> <body onload="workLoop()"> <div id="result">result goes here.</div> </body> </html> 
+4
source

since JS is single-threaded, I don’t think there is any way around. However, if you only support new browsers, you may need to examine the web workers who might spawn a new thread for work: http://developer.mozilla.org/en-US/docs/DOM/Using_web_workers

The only drawback I can think of is that I read that debugging web workers is difficult, because dev tools (Chrome Dev tools, if the dev channel does not work, firebug) does not support profiling for it.

here is a good tutorial to get you started: http://net.tutsplus.com/tutorials/javascript-ajax/getting-started-with-web-workers/

+1
source

You have an approximate working example in the 1st test, but I see a logical error. In your if () you need to return from the function, otherwise it will always run several functions competing for this thread.

 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); //!!!!! return;//without this it'll keep iterating through the for loop without waiting for the next 5ms //!!!!! } } document.getElementById("result").innerHTML = stats.a; }; 

None of the as samples wait for the next setTimeout (in the second example, you continue to repeat the for loop until the for loop completes, but for each block size you set a timeout for the next iteration. This is a delay of the entire sequence for 5 ms, they are still all pileup and execute 5 ms from the moment your for loop starts iteration)

In general, you seem to be on the right track, in both examples there are only small logical errors.

+1
source

There is a library on github available for this type of big loop without blocking the browser / stream and without using web workers.

It is somewhat experimental and supports large recursive loops, but this may work for you. Depends on q.js for resolving promises.

https://github.com/sterpe/stackless.js

0
source

This should do what you want:

 <!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 stats = {a:0,spins:0}; function doIt() { for (var chunk = 0; chunk < chunkSize; chunk++) { stats.a++; } document.getElementById("result").innerHTML = stats.a; if(++stats.spins < spins) setTimeout(doIt,5); } </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> 
-one
source

All Articles