2012-12-28 23 views
7

我有一個循環需要在瀏覽器中運行2億次。這是一個模擬器,有幾個人需要定期使用。大約需要15分鐘才能運行,但在此期間,瀏覽器會頻繁彈出警告,提示「此腳本耗時過長」等,並且在該功能期間它完全掛起Firefox。這也意味着該頁面不會更新我的狀態指示器(這只是一個數字)。阻止JavaScript在大循環上鎖定瀏覽器

我用Google搜索「的JavaScript產量」和讀取的第一個4頁命中。一些討論一個新的「yield」關鍵字,但是隻有一個描述和例子,我覺得這是不可理解的,例如, 「包含yield關鍵字的函數是一個生成器,當你調用它時,它的形式參數綁定到實際參數,但它的實體並沒有被實際評估」。 yield屈服於用戶界面嗎?

爲數不多的解決方案,我也發現這是舊的文章,它使用過時的被叫參數和定時器,稱自己: http://www.julienlecomte.net/blog/2007/10/28/

然而,上面的例子中不包含任何循環變量或狀態,當我加上它們就會崩潰,而我的淨結果總是爲零。

它也沒有做分塊,但我已經發現了一些其他的例子,其使用「索引%100 == 0」每次迭代塊。但是,這似乎是一個緩慢的做法。例如。這樣的:

How to stop intense Javascript loop from freezing the browser

但它沒有任何的方式來更新進度,不屈服的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

+4

因爲JS通常是單線程的,我不認爲有任何解決方法。但是,如果您只支持較新的瀏覽器,則可能需要查看網絡工作人員,這可能會產生一個新線程來完成工作:https://developer.mozilla.org/en-US/docs/DOM/Using_web_workers – kennypu

+0

@kennypu您應該將其作爲答案發布。 –

+1

你的性能問題很可能是常量DOM操作(每5ms),這是非常昂貴的。嘗試以不同的速率更新DOM,例如每10-20秒鐘查看一次,看看它是否會使其性能更好/更快。 – elclanrs

回答

1

,因爲JS是單線程通常情況下,我不認爲有任何辦法可以繞過它。但是,如果您只支持較新的瀏覽器,則可能需要查看網絡工作人員,這可能會產生一個新線程來執行工作:http://developer.mozilla.org/en-US/docs/DOM/Using_web_workers

我能想到的唯一缺點是我讀過它很難因爲開發工具(除非運行開發通道,螢火蟲,Chrome開發工具)調試Web Workers,不支持分析。

這裏有一個很好的教程,讓你開始:http://net.tutsplus.com/tutorials/javascript-ajax/getting-started-with-web-workers/

4

網絡工作者聽起來更好的解決方案。

我寫這個很快,所以我不知道它是否工作。性能會很差...

編輯:更改爲與海報相同的格式。經測試

<!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> 
+0

嗨rissicay,你能解釋一下「if(index)部分是什麼嗎?我理解你說的是」if(index == 0)indext = 1;「這與說」index + = 1;「相同 –

+0

你是對的,做了一個編輯 – rissicay

-1

這應該做你想要什麼:

<!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> 
1

你接近你的第一個測試工作的例子,但我確實看到一個邏輯錯誤。在你的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循環完成迭代,但在每塊大小設置超時爲後續所有這一切都是延遲整個序列5毫秒,它們仍然堆積並執行5毫秒時從你的for循環開始迭代)

總而言之,你似乎在正確的軌道上,有隻是小兩個例子中的邏輯錯誤。

+0

謝謝,這很有幫助,雖然我不明白是怎麼回事,我添加了這個返回,然後輸出每個數字(例如1,2,3)而不是100,200等等。我在返回前加上了&&(index> 0)if和一個額外的索引++,現在它似乎工作。奇怪的是,我從幾個已發佈的例子中的一個逐字拷貝了這個例子,關於如何做這個,我想他們發佈了非工作代碼 –

+0

ya,好點,因爲你開始的倍數爲100,第一次迭代需要索引> 0(0%100 == 0)。即複製/粘貼準備就緒,那些是指針,也許你使用的目標是成爲一個指針。但是對於像遞歸函數這樣的東西來說,這是一個常見的錯誤,因爲它是在JavaScript中,延遲的循環依賴像「index」這樣的全局變量。好吧。那麼你現在需要在哪裏?您的應用是否按照您的希望工作? – chwagssd

0

在github上有一個庫,可用於此類大循環,而不鎖定瀏覽器/線程並且不使用Web工作人員。

有點實驗性,並支持大的遞歸循環,但它可能適用於您。取決於解決承諾的q.js。

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