简体   繁体   English

如何在密集的JavaScript处理期间(简要地)向浏览器提供控制权?

[英]How can I give control back (briefly) to the browser during intensive JavaScript processing?

I have read the post here about using setTimeout() during intensive DOM processing (using JavaScript), but how can I integrate this function with the below code? 我在这里阅读了关于在密集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. 以下代码适用于少量选项,但当选项数量太大时,我的“请等待”动画GIF会在本地JavaScript处理时冻结。 Thanks! 谢谢!

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

Here is a solution: 这是一个解决方案:

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
}

Not as elegant as @Borgar's solution, but you get the idea. 不像@Borgar的解决方案那么优雅,但你明白了。 Basically, I am doing the same thing, but all in your one function rather than breaking it into a higher-order function like he does. 基本上,我正在做同样的事情,但是在你的一个函数中,而不是像他那样将它分解为更高阶的函数。 I like his solution, but if you don't, perhaps this will work for you. 我喜欢他的解决方案,但如果你不喜欢,也许这对你有用。


EDIT: For those that don't immediately see it, one of the main differences between this solution and @Borgar's is that this solution allows you to set the size of the 'chunks' of data that is processed between each timeout. 编辑:对于那些没有立即看到它的人来说,这个解决方案和@ Borgar之间的主要区别之一是这个解决方案允许你设置每次超时之间处理的数据“块”的大小。 @Borgar's times-out after every single member of the array is processed. @Borgar在处理完每个数组成员后的超时时间。 If I get time, I will try to create a higher-order function to handle this so it is more elegant. 如果我有时间,我会尝试创建一个更高阶的函数来处理这个,所以它更优雅。 No promises though! 虽然没有承诺! ;) ;)


EDIT: So, here is my adaptation of @Borgar's solution, which allows for setting a 'chunk' size and configuring the timeout value more easily: 编辑:所以,这是我改编的@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...
}

Hope that helps - I think this combines the best of both solutions. 希望有所帮助 - 我认为这结合了两种解决方案的优点。 Notice , the second anonymous function no longer uses the index value, but simply passes in the entire object (with the value and name properties); 注意 ,第二个匿名函数不再使用索引值,而只是传递整个对象(带有value和name属性); that makes it a bit cleaner, since the index of the current object really isn't usually that useful when iterating over things, IMO. 因为当迭代事物时,当前对象的索引通常不是那么有用,所以它会变得更清晰一些。

I am sure there are still things that could be done to make this even better, but that is left as an exercise for the reader. 我相信仍然有一些事情可以做到更好,但这仍然是读者的练习。 ;) ;)

It just so happens that I was posting about this a moment ago here . 碰巧我刚刚在这里张贴这个。 Here is a timed loop function: 这是一个定时循环函数:

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

For your situation this would be used like this: 对于您的情况,这将使用如下:

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

}

You'll want to make sure that you have a closure or some other access to the obj variable within the iteration function. 您需要确保在迭代函数中有一个闭包或对obj变量的其他访问。

Hope this helps. 希望这可以帮助。

If you need something simpler, I wrote a jQuery plugin to ease writing of asynchronous loops: jQuery Async . 如果你需要更简单的东西,我写了一个jQuery插件来简化异步循环的编写: jQuery Async

Using the plugin, your code can be rewritten as: 使用该插件,您的代码可以重写为:

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

Should help the responsiveness. 应该有助于响应。 Tweak the 'bulk' and 'delay' option for more control. 调整'批量'和'延迟'选项以获得更多控制权。

You would need to rewrite the function to cache the element list, then loop over the list using a counter of some sort. 您需要重写该函数以缓存元素列表,然后使用某种计数器循环遍历列表。

Then when the counter reaches counter % max_num_before_wait == 0, call timeout back to the function itself. 然后当计数器到达计数器%max_num_before_wait == 0时,将超时调用回函数本身。

Make sure to clear the cache and counter at the end of the complete list, or use a secondary function with an extra count parameter. 确保清除完整列表末尾的缓存和计数器,或使用带有额外计数参数的辅助功能。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM