用户界面-防止JavaScript在大循环时锁定浏览器
user interface - Prevent JavaScript from locking up browser on big loop
我有一个需要在浏览器中运行2亿次的循环。这是一个多人需要经常使用的模拟器。运行大约需要15分钟,但在此期间,浏览器会经常弹出"此脚本耗时太长"等警告,并且在功能期间完全挂起Firefox。这也意味着页面不会更新我的状态指示器(它只是一个数字)。
我用谷歌搜索了"javascript yield",并阅读了前4页的点击。有些人讨论了一个新的"yield"关键字,但只有一个描述和例子,我觉得难以理解,例如:包含yield关键字的函数是一个生成器。当你调用它时,它的形式参数被绑定到实际参数,但它的主体并不被实际计算。yield
是否屈服于UI?
我找到的少数解决方案之一是这篇旧文章,它使用了已弃用的callee参数和一个计时器来调用自己: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>
这里是另一个尝试,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>
我花了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>
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);
}
结果输出:0
网络工作者似乎是更好的解决方案。
我写得很快,所以我不知道它是否有效。性能会很差…
编辑:改成和海报一样的格式。测试
<!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>
由于JS通常是单线程的,我认为没有任何方法可以绕过它。如果你只支持较新的浏览器,你可能想看看web worker,它可以生成一个新线程来完成工作:http://developer.mozilla.org/en-US/docs/DOM/Using_web_workers
我能想到的唯一缺点是,我读到很难调试Web Workers,因为开发工具(Chrome开发工具,除非运行dev channel, firebug)不支持它的分析。
这里有一个很好的入门教程:http://net.tutsplus.com/tutorials/javascript-ajax/getting-started-with-web-workers/
您在第一次测试中接近一个工作示例,但我确实看到了逻辑错误。在if()中,你需要从函数返回,否则它将总是运行多个函数来竞争该线程。
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;
};
两个样本都没有等待下一个setTimeout(在第二个示例中,您继续迭代for循环,直到for循环完成,但是在每个块大小上都为后续迭代设置超时。)这只会将整个序列延迟5毫秒,它们仍然会在for循环开始迭代后的5毫秒内堆积并执行)
总而言之,你似乎是在正确的轨道上,只是在两个例子中有一些小的逻辑错误。
github上有一个库可以用于这种类型的大循环,而不需要锁定浏览器/线程,也不需要使用web-workers。
有点实验性,支持大的递归循环,但它可能适合您。依赖于q.js解析承诺。
https://github.com/sterpe/stackless.js这应该是你想要的:
<!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>
- 访问布局信息是否也会导致浏览器重排
- 内部分区字体大小获胜'调整浏览器窗口大小时不会随媒体查询而更改
- 如何使用phaser使html5游戏在移动设备浏览器上运行
- 如何在Javascript执行后防止浏览器锁定
- Web 工作线程中的同步 XHR 请求是否仍会锁定浏览器
- 在循环锁定浏览器时暂停执行(用fiddles更新)
- 如何执行“;“同步”;javascript调用服务器而不锁定浏览器
- Jquery:Ajax 请求看起来是同步的,锁定浏览器
- 加载外部脚本时锁定浏览器
- JavaScript锁定功能,以防止递归(浏览器冻结/性能)
- 异步ajax请求锁定浏览器
- 在(同步)Ajax期间停止浏览器锁定
- 同步Ajax请求”;锁定“;浏览器
- jQuery正在锁定浏览器
- 大型表单或javascript锁定浏览器
- 在打开选项卡期间通过Jquery锁定浏览器当前选项卡URL栏
- 防止递归函数锁定浏览器
- 在不锁定浏览器的情况下,将大数据JSON从REST请求反序列化为对象
- 使用jquery $.Ajax使用arbor.js导致浏览器锁定,但是'native'JSON并# 39
- 用户界面-防止JavaScript在大循环时锁定浏览器