2016-11-22 1 views
1

JavaScriptで長時間実行するタスクがありますが、hereと同様のネストされた一連の入れ子に分割されます。ただし、呼び出しごとにsetTimeoutは4ms以上の遅延を追加します。この動作はwell knownであり、ブラウザによって異なります。長時間実行されているJavaScriptタスクを分割するときにsetTimeoutを強制的に遅延させないようにする

各チャンクの処理時間を50ミリ秒以下に抑えようとすると、これらの遅延が合計処理時間を少なくとも10%増加させます。

私の質問はです:ES3ブラウザや古いIEブラウザとの下位互換性を維持しながら、追加の遅延を回避して処理速度を向上させることはできますか?

答えて

2

この問題には簡単な回避策があります。 setTimeoutの最小遅延はタイマーが設定されてから測定されるので、各チャンクが処理される前にタイマーを少なくとも10 – 15ミリ秒に設定してください。 setTimeoutが複数設定されている場合、それらはキューに入れられ、次のキューは直前の直後に呼び出され、追加の遅延は発生しません。これは、2つのアクティブタイマーで行うことができます。

以下
function runLongTask() { 
    var complete = false; 
    function processChunk() { 
    if(!complete) { 
     /* ... process chunk, set complete flag after last chunk ... */ 
     //set new timer 
     setTimeout(processChunk); 
    } else { 
     /* ... code to run on completion ... */ 
    } 
    } 
    //set a timer to start processing 
    setTimeout(processChunk); 
    //set an extra timer to make sure 
    //there are always 2 active timers, 
    //this removes the extra delay provided 
    //that processing each chunk takes longer 
    //than the forced delay 
    setTimeout(processChunk); 
} 

は、各チャンクが処理された後、新たなsetTimeoutを設定する伝統的なアプローチと回避策のアプローチを比較する作業のデモです。回避策では、常に余分にsetTimeoutが設定され、各チャンクの処理時間は、各チャンクが少なくとも4ミリ秒かかっていれば、各チャンクで約40ミリ秒以上(10チャンクで約40ミリ秒以上)プロセス。この回避策では、アクティブタイマーを2つだけ使用する方法を示しています。

function runForAtLeast15ms() { 
 
    var d = (+new Date) + 15; 
 
    while(+new Date < d); 
 
} 
 

 
function testTimeout(repetitions, next, workaround) { 
 
    var startTime = +new Date; 
 

 
    function runner() { 
 
    if(repetitions > 0) { 
 
     //process chunk 
 
     runForAtLeast15ms(); 
 
     //set new timer 
 
     setTimeout(runner); 
 
    } else if(repetitions === 0) { 
 
     //report result to console 
 
     console.log((workaround? 'Workaround' : 'Traditional') + 
 
        ' approach: ' + 
 
        ((+new Date) - startTime) + ' ms'); 
 
     //invoke next() function if provided 
 
     next && next(); 
 
    } 
 
    repetitions--; 
 
    } 
 

 
    setTimeout(runner); 
 

 
    if(workaround){ 
 
    //make sure that there are always 2 
 
    //timers running by setting an extra timer 
 
    //at start 
 
    setTimeout(runner); 
 
    } 
 
} 
 

 
//First: repeat runForAtLeast15ms 10 times 
 
//with repeated setTimeout 
 
testTimeout(10, function(){ 
 
    //Then: repeat runForAtLeast15ms 10 times 
 
    //using a repeated set of 2 setTimeout 
 
    testTimeout(10, false, true); 
 
});

+0

あなたが質問をし、同時に独自の質問に答えました。 – Erevald

+0

@Everyd、そうです。 [それはSOによって奨励されています](https://stackoverflow.blog/2011/07/its-ok-to-ask-and-answer-your-own-questions/)。私は最近自分自身でこの質問に苦労し、私が文書化して共有したい解決策を見つけました。共有することで、誰かがより良い解決策を提供することさえできます。 –

関連する問題