Как я могу вернуть контроль (кратко) браузеру во время интенсивной обработки JavaScript? - PullRequest
22 голосов
/ 17 октября 2008

Я прочитал пост здесь об использовании setTimeout () во время интенсивной обработки DOM (с использованием JavaScript), но как я могу интегрировать эту функцию с приведенным ниже кодом? Приведенный ниже код прекрасно работает для небольшого числа опций, но когда количество опций становится слишком большим, мой анимированный GIF «пожалуйста, подождите» зависает, пока обрабатывается локальный JavaScript. Спасибо!

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 ]

29 голосов
/ 17 октября 2008

Вот решение:

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's решение, но вы поняли идею. По сути, я делаю то же самое, но все в одной функции, а не разбиваю ее на функцию более высокого порядка, как он. Мне нравится его решение, но если вы этого не сделаете, возможно, это сработает для вас.


РЕДАКТИРОВАТЬ: Для тех, кто не видит его сразу, одно из основных отличий этого решения от @ Borgar заключается в том, что это решение позволяет вам устанавливать размер «кусков» данных, которые обрабатывается между каждым таймаутом. Тайм-аут @ Боргара после обрабатывается каждый отдельный элемент массива. Если у меня будет время, я постараюсь создать функцию более высокого порядка, чтобы справиться с этим, чтобы она была более элегантной. Никаких обещаний! ;)


РЕДАКТИРОВАТЬ: Итак, вот моя адаптация @ Borgar's решения, которое позволяет более легко установить размер чанка и настроить значение тайм-аута:

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.

Я уверен, что есть еще вещи, которые можно сделать, чтобы сделать это еще лучше, но это оставлено в качестве упражнения для читателя. ;)

20 голосов
/ 17 октября 2008

Так получилось, что я писал об этом минуту назад здесь . Вот функция синхронизированного цикла:

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 в итерационной функции.

Надеюсь, это поможет.

4 голосов
/ 23 октября 2009

Если вам нужно что-то более простое, я написал плагин 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++;
        }
    });
    /////////////////////////////
}

Должна помочь отзывчивость. Настройте параметры «Объем» и «Задержка» для большего контроля.

0 голосов
/ 17 октября 2008

Вам потребуется переписать функцию для кэширования списка элементов, а затем перебрать список с помощью какого-нибудь счетчика.

Затем, когда счетчик достигнет счетчика% max_num_before_wait == 0, вызовите тайм-аут обратно к самой функции.

Обязательно очистите кэш и счетчик в конце полного списка или используйте дополнительную функцию с дополнительным параметром count.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...