【问题标题】:Avoid forced delay of setTimeout when breaking up long running JavaScript tasks在分解长时间运行的 JavaScript 任务时避免强制延迟 setTimeout
【发布时间】:2016-11-22 22:23:28
【问题描述】:

我在 JavaScript 中有一个长时间运行的任务,我用一系列嵌套的setTimeout(processChunk, 0) 将其分解成块,类似于here 的描述。但是,对于每次调用,setTimeout 会增加 4 毫秒或更多的额外延迟。这种行为是well known,并且因浏览器而异。

当我尝试将每个块的处理时间保持在 50 毫秒或更短时,这些额外的延迟会使总处理时间至少增加 10%。

我的问题是:我能否在保持向后兼容 ES3 浏览器和旧 IE 浏览器的同时避免额外的延迟(从而提高处理速度)?

【问题讨论】:

    标签: javascript performance internet-explorer settimeout


    【解决方案1】:

    这个问题有一个简单的解决方法。由于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 毫秒或更多(10 个块大约 40 毫秒或更多,如下所示),前提是每个块至少需要 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);
    });

    【讨论】:

    • 您在提出问题的同时回答了自己的问题。
    • @Everald,没错。 It is encouraged by SO。我最近自己在这个问题上苦苦挣扎,并找到了一个我想记录和分享的解决方案。通过分享,有人甚至可以提供更好的解决方案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-02-06
    • 2019-02-20
    • 1970-01-01
    • 1970-01-01
    • 2016-09-18
    • 1970-01-01
    • 2015-12-30
    相关资源
    最近更新 更多