如何在密集的 JavaScript 处理期间将控制权(简要地)交还给浏览器?

IT技术 javascript jquery
2021-02-04 17:52:38

我阅读后这里约密集型DOM处理(使用JavaScript)期间使用的setTimeout(),但我怎么能与下面的代码集成这个功能呢?The below code works fine for a small number of options, but when the number of options gets too big my "please wait" animated GIF freezes while the local JavaScript is processing. 谢谢!

function appendToSelect() {
    $("#mySelect").children().remove() ;
    $("#mySelect").html(
        '<option selected value="' + obj.data[0].value + '">'
        + obj.data[0].name
        + '</option>'
    );
    var j = 1 ;
    for (var i = 1; i < obj.data.length; i++) {
        $("#mySelect").append(
            '<option value="' + obj.data[i].value + '">'
            + obj.data[i].name
            + '</option>'
        ); 
    }
}
4个回答

这是一个解决方案:

function appendToSelect() {
  $("#mySelect").children().remove();
  $("#mySelect").html(
    '<option selected value="'+obj.data[0].value+'">'
    + obj.data[0].name
    + '</option>'
  );
  obj.data.splice(0, 1); // we only want remaining data
  var appendOptions = function() {
    var dataChunk = obj.data.splice(0, 10); // configure this last number (the size of the 'chunk') to suit your needs
    for(var i = 0; i < dataChunk.length; i++) {
      $("#mySelect").append(
        '<option value="' + dataChunk[i].value + '">'
        + dataChunk[i].name
        + '</option>'
      );
    }
    if(obj.data.length > 0) {
      setTimeout(appendOptions, 100); // change time to suit needs
    }
  };
  appendOptions(); // kicks it off
}

不像@Borgar 的解决方案那么优雅,但你明白了。基本上,我正在做同样的事情,但都在你的一个函数中,而不是像他那样将它分解成一个更高阶的函数。我喜欢他的解决方案,但如果你不喜欢,也许这对你有用。


编辑:对于那些没有立即看到它的人,此解决方案与@Borgar 的主要区别之一是此解决方案允许您设置在每次超时之间处理的数据“块”的大小。@Borgar处理数组的每个成员后超时如果我有时间,我会尝试创建一个高阶函数来处理这个问题,这样它就更优雅了。虽然没有Promise!;)


编辑:所以,这是我对@Borgar解决方案的改编,它允许设置“块”大小并更轻松地配置超时值:

function incrementallyProcess(workerCallback, data, chunkSize, timeout, completionCallback) {
  var itemIndex = 0;
  (function() {
    var remainingDataLength = (data.length - itemIndex);
    var currentChunkSize = (remainingDataLength >= chunkSize) ? chunkSize : remainingDataLength;
    if(itemIndex < data.length) {
      while(currentChunkSize--) {
        workerCallback(data[itemIndex++]);
      }
      setTimeout(arguments.callee, timeout);
    } else if(completionCallback) {
      completionCallback();
    }
  })();
}

function appendToSelect() {
  $("#mySelect").children().remove();
  $("#mySelect").html(
    '<option selected value="' + obj.data[0].value + '">'
    + obj.data[0].name
    + '</option>'
  );
  obj.data.splice(0,1); // we only want remaining data      
  incrementallyProcess(function(data) {
    $("#mySelect").append(
    '<option value="' + data.value + '">'
    + data.name
    + '</option>'
   );
  }, obj.data, 10, 100, removeAnimatedGifFunction); // last function not required...
}

希望有所帮助 - 我认为这结合了两种解决方案的优点。注意,第二个匿名函数不再使用索引值,而是简单地传入整个对象(带有 value 和 name 属性);这使它更清晰,因为当前对象的索引在迭代事物时通常不是那么有用,IMO。

我相信还有一些事情可以做来使这更好,但这留给读者作为练习。;)

碰巧的是,我刚刚在这里发布了有关此内容的信息这是一个定时循环函数:

function processLoop( actionFunc, numTimes, doneFunc ) {
  var i = 0;
  var f = function () {
    if (i < numTimes) {
      actionFunc( i++ );  // closure on i
      setTimeout( f, 10 )
    } 
    else if (doneFunc) { 
      doneFunc();
    }
  };
  f();
}

对于您的情况,这将像这样使用:

function appendToSelect () {

  $("#mySelect").children().remove() ;
  $("#mySelect").html(
      '<option selected value="' + obj.data[0].value + '">'
      + obj.data[0].name
      + '</option>'
  );
  var j = 1 ;

  processLoop(function (i){
    $("#mySelect").append(
        '<option value="' + obj.data[i].value + '">'
        + obj.data[i].name
        + '</option>'
    ); 
  }, obj.data.length);

}

您需要确保在迭代函数中拥有对 obj 变量的闭包或其他访问权限。

希望这可以帮助。

如果你需要更简单的东西,我写了一个 jQuery 插件来简化异步循环的编写:jQuery Async

使用该插件,您的代码可以重写为:

function appendToSelect() {
    $("#mySelect").children().remove() ;
    $("#mySelect").html(
        '<option selected value="' + obj.data[0].value + '">'
        + obj.data[0].name
        + '</option>'
    );

    /////////////////////////////
    var i = 1;
    $.whileAsync({
        test: function(){ i < obj.data.length; }
        loop: function()
        {
            $("#mySelect").append(
                '<option value="' + obj.data[i].value + '">'
                + obj.data[i].name
                + '</option>'
            ); 
            i++;
        }
    });
    /////////////////////////////
}

应该有助于响应。调整“批量”和“延迟”选项以获得更多控制。

您需要重写函数以缓存元素列表,然后使用某种计数器循环遍历列表。

然后当计数器达到计数器 % max_num_before_wait == 0 时,将 timeout 调用回函数本身。

确保清除完整列表末尾的缓存和计数器,或使用带有额外计数参数的辅助函数。