7

ブラウザで 2 億回実行する必要があるループがあります。複数の人が定期的に使用する必要があるシミュレーターです。実行には約 15 分かかりますが、この間、ブラウザは「このスクリプトは時間がかかりすぎています」などの警告を頻繁にポップアップ表示し、機能中に Firefox を完全にハングさせます。これは、ページがステータス インジケーター (単なる数字) を更新しないことも意味します。

「javascript yield」をグーグルで検索し、ヒットの最初の 4 ページを読みました。新しい「yield」キーワードについて議論する人もいますが、説明と例が 1 つしかなく、理解できないと思います。たとえば、「yield キーワードを含む関数はジェネレーターです。呼び出すと、仮パラメーターは実引数にバインドされますが、体は実際には評価されません」。yieldUI に屈しますか?

私が見つけたいくつかの解決策の 1 つは、非推奨の 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 時間費やしました。何か案は?

何人かの人々が Web ワーカーを提案しています。私は彼が機能するように数日間試みましたが、数値などを渡す同様の例を見つけることができませんでした.以下のコードは、それを機能させるための私の最後の試みでしたが、結果は常に 0 です 100000.つまり、失敗します上記の 2 番目の例が失敗するのと同じように。

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

5 に答える 5

4

Web ワーカーの方が優れたソリューションのように思えます。

私はこれをすぐに書いたので、うまくいくかどうかはわかりません。性能はかなり悪くなります…

編集:ポスターと同じフォーマットに変更。テスト済み

<!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>
于 2012-12-28T02:42:40.337 に答える
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 をそのまま待機しません (2 番目の例では、for ループが完了するまで for ループを反復し続けますが、各ブロック サイズで後続の反復のタイムアウトを設定します。これは遅延だけです。 5 ミリ秒のシーケンス全体、for ループの反復が開始されてから 5 ミリ秒の間、それらはすべてパイルアップして実行されます)

全体として、正しい方向に進んでいるように見えますが、両方の例に小さな論理エラーがあります。

于 2012-12-28T02:57:44.490 に答える
1

JS は通常シングル スレッドなので、それを回避する方法はないと思います。ただし、新しいブラウザーのみをサポートしている場合は、新しいスレッドを生成して作業を実行できる Web ワーカーを調べることをお勧めします: http://developer.mozilla.org/en-US/docs/DOM/Using_web_workers

私が考えることができる唯一の欠点は、開発ツール (開発チャネル、firebug を実行していない限り Chrome Dev ツール) がプロファイリングをサポートしていないため、Web ワーカーをデバッグするのが難しいことを読んだことです。

ここにあなたが始めるための素晴らしいチュートリアルがあります: http://net.tutsplus.com/tutorials/javascript-ajax/getting-started-with-web-workers/

于 2012-12-28T02:40:16.367 に答える
0

ブラウザ/スレッドをロックせず、Web ワーカーを使用せずに、このタイプの大きなループに使用できる github のライブラリがあります。

やや実験的で、大きな再帰ループをサポートしていますが、うまくいくかもしれません。promise の解決は q.js に依存します。

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

于 2013-04-06T18:43:54.863 に答える
-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>
于 2012-12-28T02:45:46.593 に答える