この問題には簡単な回避策があります。 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);
});
あなたが質問をし、同時に独自の質問に答えました。 – Erevald
@Everyd、そうです。 [それはSOによって奨励されています](https://stackoverflow.blog/2011/07/its-ok-to-ask-and-answer-your-own-questions/)。私は最近自分自身でこの質問に苦労し、私が文書化して共有したい解決策を見つけました。共有することで、誰かがより良い解決策を提供することさえできます。 –