Я прочитал сообщение здесь об использовании 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>'
);
}
}
Ответ 1
Вот решение:
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 решение, но вы получите эту идею. В принципе, я делаю то же самое, но все в вашей одной функции, а не разбиваю ее на функцию более высокого порядка, как он. Мне нравится его решение, но если вы этого не сделаете, возможно, это сработает для вас.
EDIT: для тех, кто этого не видит сразу, одним из основных различий между этим решением и @Borgar's является то, что это решение позволяет вам установить размер "кусков" данных, которые обрабатываются между каждым таймаутом. @Borgar's после каждого отдельного элемента массива обрабатывается. Если у меня будет время, я попытаюсь создать функцию более высокого порядка, чтобы справиться с этим, чтобы она была более элегантной. Нет promises, хотя!;)
EDIT: Итак, вот моя адаптация @Borgar's, которая позволяет более легко установить размер "chunk" и настроить значение тайм-аута:
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...
}
Надеюсь, что это поможет - я думаю, что это сочетает в себе лучшее из обоих решений. Уведомление, вторая анонимная функция больше не использует значение индекса, а просто передает весь объект (со значениями и свойствами имени); что делает его немного более чистым, так как индекс текущего объекта действительно не всегда полезен при итерации над вещами, IMO.
Я уверен, что все еще есть вещи, которые можно сделать, чтобы сделать это еще лучше, но это остается для упражнения читателем.;)
Ответ 2
Просто так случилось, что я опубликовал об этом минуту назад здесь. Ниже приведена функция синхронизации по времени:
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 в функции итерации.
Надеюсь, что это поможет.
Ответ 3
Если вам нужно что-то более простое, я написал плагин 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++;
}
});
/////////////////////////////
}
Должна помогать реагированию. Измените параметр "bulk" и "delay" для большего контроля.
Ответ 4
Вам нужно будет переписать функцию для кэширования списка элементов, а затем перебрать список с помощью какого-либо счетчика.
Затем, когда счетчик достигает счетчика% max_num_before_wait == 0, время ожидания вызова возвращается к самой функции.
Обязательно очистите кеш и счетчик в конце полного списка или используйте вторичную функцию с дополнительным параметром count.