Avoid forced delay of setTimeout when breaking up long running JavaScript tasks

谁都会走 提交于 2019-12-11 04:14:29

问题


I have a long running task in JavaScript, which I break up in chunks with a series of nested setTimeout(processChunk, 0), similar to what is described here. However, for each invocation, setTimeout adds an additional delay of 4 ms or more. This behaviour is well known and varies across browsers.

When I attempt to keep the processing time of each chunk at 50 ms or less, these additional delays increase total processing time by at least 10%.

My question is: Can I avoid the additional delay (and thus improve the processing speed) while maintaining backwards compatibility with ES3 browsers and old IE browsers?


回答1:


There is a straightforward workaround for this issue. Since the minimum delay of setTimeout is measured from when the timer is set, make sure to set timers at least 10–15 ms before each chunk should be processed. When several setTimeout are set, they queue up and the next one is invoked immediately after the previous, without the additional delay. This can be done with only 2 active timers:

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);
}

Below is a working demo comparing the workaround approach to the traditional approach of setting a new setTimeout after each chunk is processed. In the workaround there is always an extra setTimeout set ahead, reducing the processing time with about 4 ms or more for each chunk (about 40 ms or more for 10 chunks, as demonstrated below), provided that each chunk takes at least 4 ms to process. Note that the workaround demonstrates the use of only 2 active timers.

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);
});


来源:https://stackoverflow.com/questions/40752743/avoid-forced-delay-of-settimeout-when-breaking-up-long-running-javascript-tasks

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!