9

環境

更新にそれぞれ 5 秒かかる複雑なグラフが約 10 個あります。これらの 10 個のグラフをループすると、更新に約 50 秒かかります。この 50 秒間、ユーザーはスクロールバーを動かすことができます。スクロールバーが移動した場合、リフレッシュは停止する必要があり、スクロールバーの移動が停止すると、リフレッシュが再び発生します。

インターフェイスを更新するために、ループ内で setTimeout 関数を使用しています。アルゴリズムは次のとおりです。

  • 最初のグラフをレンダリングする
  • setTimeout(2 番目のグラフをレンダリング、200)
  • 2 番目のグラフがレンダリングされると、3 番目のグラフが 200 ミリ秒でレンダリングされます。

setTimeout を使用すると、スクロールバー イベントをキャッチし、次の更新時に clearTimeout して、スクロールバーを移動する前に 50 秒待機することを回避できます...

問題は、いつでも実行できないことです。

次の簡単なコードを使用してください (このフィドルで試すことができます: http://jsfiddle.net/BwNca/5/ ):

HTML :

<div id="test" style="width: 300px;height:300px; background-color: red;">

</div>
<input type="text" id="value" />
<input type="text" id="value2" />

Javascript :

var i = 0;
var j = 0;
var timeout;
var clicked = false;

// simulate the scrollbar update : each time mouse move is equivalent to a scrollbar move
document.getElementById("test").onmousemove = function() {

    // ignore first move (because onclick send a mousemove event)
    if (clicked) {
        clicked = false;
        return;
    }

    document.getElementById("value").value = i++; 
    clearTimeout(timeout);
}

// a click simulates the drawing of the graphs
document.getElementById("test").onclick = function() {
    // ignore multiple click
    if (clicked) return;

    complexAlgorithm(1000);    
    clicked = true;   
}

// simulate a complexe algorithm which takes some time to execute (the graph drawing)
function complexAlgorithm(milliseconds) {
  var start = new Date().getTime();
  for (var i = 0; i < 1e7; i++) {
    if ((new Date().getTime() - start) > milliseconds){
      break;
    }
  }   

  document.getElementById("value2").value = j++;

  // launch the next graph drawing
  timeout =  setTimeout(function() {complexAlgorithm(1000);}, 1);
}

コードは次のことを行います。

  • マウスを赤い div に移動すると、カウンターが更新されます
  • 赤い div をクリックすると、1 秒の大きな処理がシミュレートされます (そのため、javascript のモノ スレッドが原因でインターフェイスがフリーズします)。
  • フリーズ後、マウスが動くまで1ms待ち、処理のやり直し等を行う
  • マウスが再び動くと、タイムアウトを中断して無限ループを回避します。

問題

フリーズ中に1回クリックしてマウスを動かすと、setTimeoutが発生したときに実行される次のコードは、mousemoveイベントのコードであると考えていました(したがって、タイムアウトとフリーズをキャンセルします)しかし、時々クリックのカウンターは、movemove イベントにより 1 ポイントだけではなく、2 ポイント以上獲得します...

このテストの結論: setTimeout 関数は、mousemove イベント中にコードを実行するために常にリソースを解放するとは限りませんが、別のコードを実行する前にスレッドを保持し、settimeout コールバック内でコードを実行することがあります。

これの影響は、実際の例では、ユーザーがスクロールバーを使用する前に 5 秒待つのではなく、10 秒待つことができることです (2 つのグラフがレンダリングされます)。これは非常に煩わしく、これを回避し、レンダリング フェーズ中にスクロールバーが移動したときに 1 つのグラフのみがレンダリングされる (他のグラフはキャンセルされる) ようにする必要があります。

マウスが動いたときにタイムアウトを確実に破るにはどうすればよいですか?

PS: 以下の単純な例では、タイムアウトを 200 ミリ秒に更新すると、すべてが完全に実行されますが、許容できる解決策ではありません (実際の問題はより複雑で、問題は 200 ミリ秒のタイマーと複雑なインターフェイスで発生します)。「グラフのレンダリングを最適化する」などの解決策を提供しないでください。これはここでは問題ではありません。

EDIT:cernunnosには問題のより良い説明があります:また、ループ上のプロセスを「ブロック」することで、そのループが終了するまでイベントを処理できないようにするため、イベントはその間にのみ処理されます(そしてタイムアウトはクリアされます)各ループの実行(したがって、中断する前に2回以上の完全な実行を待たなければならないことがあります) .

問題は太字の言葉で正確に含まれています: 必要なときに必ず実行を中断し、中断する前に2回以上の完全な実行を待たないようにしたい


2番目の編集:

要約すると、次の jsfiddle を取ります: http://jsfiddle.net/BwNca/5/ (上記のコード)。

この jsfiddle を更新して、次のソリューションを提供します。

赤い div でマウスを動かします。次に、クリックして移動を続けます。右のカウンターは 1 回だけ上げる必要があります。しかし、最初のカウンターが再び実行される前に、2 回または 3 回レイズすることがあります...これが問題です。1 回だけレイズする必要があります。

4

7 に答える 7

2

ここでの大きな問題は、 setTimeout が開始されると、特に負荷の高い作業を行っている場合は予測できないことです。

ここでデモを見ることができます: http://jsfiddle.net/wao20/C9WBg/

var secTmr = setTimeout(function(){
    $('#display').append('Timeout Cleared > ');
    clearTimeout(secTmr);        

    // this will always shown
    $('#display').append('I\'m still here! ');
}, 100);

ブラウザのパフォーマンスへの影響を最小限に抑えるためにできることは 2 つあります。

setTimeoutID のすべてのインスタンスを保存し、停止するときにループします。

var timers = []

// When start the worker thread
timers.push( setTimeout(function () { sleep(1000);}, 1) );

// When you try to clear 
while (timers.length > 0) {
     clearTimeout(timers.pop());
}

clearTimeout がタイマーの停止に失敗した場合に備えて、プロセスを停止しようとするときにフラグを設定し、ワーカー スレッド内でそのフラグを確認します。

// Your flag 
var STOPForTheLoveOfGod = false;

// When you try to stop 
STOPForTheLoveOfGod = true; 
while (timers.length > 0) {       
     clearTimeout(timers.pop()); 
}

// Inside the for loop in the sleep function 
function sleep(milliseconds) {
    var start = new Date().getTime();
    for (var i = 0; i < 1e7; i++) {
        if (STOPForTheLoveOfGod) {
            break;
        }       

        // ...  
    }
}

この新しいスクリプトを試すことができます。 http://jsfiddle.net/wao20/7PPpS/4/

于 2013-04-20T16:44:43.767 に答える
0

驚くべきことに、setTimeout(); その後、チェックを入れることができます。変数が true の場合、待機を破棄するか、破棄します。現在、スクロールバーでスクロールすることを確認できる方法があります。手段を使用して変数内で真であることを確認した後、バーをスクロールするときにこれが無限に繰り返され、5 秒の実行時間が何度もあることがわかります。これを修正するには、1 秒待機して、重複しないようにします。どういたしまして :)

于 2013-04-17T01:10:52.097 に答える
0

答えはあなたの質問にあります

...更新を停止する必要があり、スクロールバーの移動が停止すると、更新が再び発生します。

complexAlgorithm は、途中でほぼ即座にブレーキをかけることができるように記述する必要があります (再実行する必要があることがわかっている場合)。

メインコードは次のようになります

stopAllRefresh;  //should instantly(or after completing small chunk) stop refresh
setTimeout(startRefresh, 100);

setTimeout のようにグラフを小さなチャンク (各ラン < 1 秒) でレンダリングします。

var curentGraph = 0;
var curentChunk = 0;

function renderGraphChunk(){
    if (needToBreak)  //check if break rendering
    {exit};
// Render chunk here
    render(curentGraph, curentChunk);
    curentChunk +=1;

    setTimeout(renderGraphChunk, 1);
}

これは単なるアイデアスケッチであり、実際の実装は完全に異なる場合があります

于 2013-04-19T17:45:43.743 に答える