防止JavaScript在大循环上锁定浏览器

Joh*_*tle 7 javascript user-interface

我有一个循环,需要在浏览器中运行2亿次.这是一个模拟器,几个人需要定期使用.运行大约需要15分钟,但在此期间,浏览器会经常弹出"此脚本耗时太长"等警告,并在功能期间完全挂起Firefox.这也意味着页面不会更新我的状态指示器(这只是一个数字).

我用google搜索"javascript yield"并阅读前4页的点击量.有些人讨论了一个新的"yield"关键字,但是只有一个描述和示例,我觉得这是不可理解的,例如"包含yield关键字的函数是一个生成器.当你调用它时,它的形式参数绑定到实际的参数,但是它的身体实际上没有被评估".是否yield屈服于UI?

我找到的为数不多的解决方案之一就是这个旧帖子,它使用不赞成的被调用者参数和一个计时器来调用自己:http: //www.julienlecomte.net/blog/2007/10/28/

但是,上面的例子不包含任何循环变量或状态,当我添加它们时它会分崩离析,我的净结果总是为零.

它也没有进行分块,但是我发现了一些其他的例子,它们在每次迭代时使用"index%100 == 0".然而,这似乎是一种缓慢的做法.例如:

如何阻止激烈的Javascript循环冻结浏览器

但它没有任何方式来更新进度,也没有屈服于UI(因此仍然挂起浏览器).这是一个在执行期间挂起浏览器的测试版本:

<!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>
Run Code Online (Sandbox Code Playgroud)

这是另一个stats.a始终为零的尝试(所以我认为存在一些范围问题):

<!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>
Run Code Online (Sandbox Code Playgroud)

我花了48个小时试图让这个工作 - 要么我非常愚蠢,要么这很难.有任何想法吗?

有几个人建议网络工作者.我试了好几天才能完成他的工作,但我找不到一个类似的例子,它传递了一个数字等.下面的代码是我最后一次尝试让它工作,但结果总是0,当它应该是100000.即它失败就像我上面的第二个例子失败一样.

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>
Run Code Online (Sandbox Code Playgroud)

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);
}
Run Code Online (Sandbox Code Playgroud)

结果输出:0

ken*_*ypu 1

由于 JS 通常是单线程的,我认为没有任何办法可以解决它。但是,如果您仅支持较新的浏览器,您可能需要研究 Web Worker,它可以生成一个新线程来完成工作: http ://developer.mozilla.org/en-US/docs/DOM/Using_web_workers

我能想到的唯一缺点是,我读到很难调试 Web Workers,因为开发工具(Chrome 开发工具,除非运行开发通道、firebug)不支持对其进行分析。

这是一个很好的教程,可以帮助您入门:http://net.tutsplus.com/tutorials/javascript-ajax/getting-started-with-web-workers/