Существует простой способ обхода проблемы. Поскольку минимальная задержка 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 кусков, как показано ниже) при условии, что каждый кусок занимает не менее 4 мс до обработать. Обратите внимание, что обходной путь демонстрирует использование только 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
@ Эвералд, это верно. [Это рекомендуется SO] (https://stackoverflow.blog/2011/07/its-ok-to-ask-and-answer-your-own-questions/). Недавно я боролся с этим вопросом и нашел решение, которое я хотел бы документировать и делиться. Поделившись, кто-то может даже предложить лучшее решение. –