В предыдущей статье мы изучали время выполнения JavaScript в браузерах и их ограничения [3], а также методы, которые могут помочь избежать появления сообщений вида "Скрипт не отвечает" в браузере. Мы рассмотрели метод псевдо-потоков, основанный на времени выполнения. Теперь мы будем искать способ обработки больших массивов данных в браузерах.
Несколько лет назад разработчики даже не задумывались об альтернативах сложной обработки данных на стороне сервера. Теперь же все изменилось, и многие Ajax-приложения обмениваются большим количеством данных между клиентом и сервером. Кроме того, программный код приложения может изменять объектную модель документа (DOM), что требует особенно много времени для обработки браузером. Однако, попытка обработки больших объемов информации за один раз может привести к зависанию приложения и принудительному завершению работы приложения со стороны браузера.
Таймеры, существующие в JavaScript, могут помочь предотвратить блокировку приложения браузером благодаря разделению длительных по времени процессов обработки данных на небольшие блоки. Вот наша функция обработки больших массивов с данными:
function ProcessArray(data, handler, callback) {
Функция ProcessArray()
имеет три аргумента:
data
: массив элементов для обработки;handler
: функция, применяемая для обработки каждого отдельного элемента массива;callback
: дополнительная функция, вызываемая после полной обработки массива.
Теперь мы определим переменные конфигурации:
var maxtime = 100; // время обработки блоков массива var delay = 20; // задержка между двумя процессами обработки блоков var queue = data.concat(); // копия исходного массива
Переменная maxtime
определяет максимальное время в миллисекундах, разрешенное для обработки каждого блока массива. Переменная delay
задает время в миллисекундах между процессами обработки отдельных блоков массива. Переменная queue
является копией исходного массива с данными, в которой в принципе нет необходимости в большинстве случаев. Однако, так как массив передается в функцию по ссылке, и мы с каждой обработкой нового элемента выталкиваем его из массива функцией shift
, эта копия в нашем случае нужна для сохранности данных исходного массива во время обработки.
Теперь мы можем использовать функцию setTimeout
для начала обработки:
setTimeout(function() { var endtime = +new Date() + maxtime; do { handler(queue.shift()); } while (queue.length > 0 && endtime > +new Date());
Сначала вычисляется значение переменной endtime
— время перкращения обработки. В цикле do…while
происходит обработка элементов массива queue
в порядке очереди, пока обработка массива полностью не завершится или же не будет достигнуто время остановки endtime
.
Примечение: Зачем использовать цикл do…while
?
В JavaScript поддерживается оба вида циклов while: while
и do…while
. Отличие между ними в том, что цикл do…while
гарантирует нам выполнение хотя бы одной итерации цикла. Если же мы используем обычный цикл while
, то цикл может даже и не начаться из-за недостаточного времени, указанного в переменной endtime
.
Наконец, мы определяем, остались ли элементы в массиве, которые нужно обработать, и, если обработка необходима, вызываем функцию обработки массива после короткой задержки:
if (queue.length > 0) { setTimeout(arguments.callee, delay); } else { if (callback) callback(); } }, delay); } // конец функции ProcessArray
Функция callback
выполнится один раз, после обработки всех элементов массива.
Теперь можно протестировать функцию ProcessArray()
на сгенерированном наборе данных:
// обработка отдельного элемента массива function Process(dataitem) { console.log(dataitem); } // функция, вызываемая после завершения обработки function Done() { console.log("Готово"); } // тестовые данные var data = []; for (var i = 0; i < 500; i++) data[i] = i; // обработка элементов массива ProcessArray(data, Process, Done);
Приведенный код работает во всех браузерах, включая IE6+. Это эффективное кроссбраузерное решение, однако в будущем HTML5 предоставит нам более подходящее решение! В следующей статье мы рассмотрим многопоточность в HTML 5…
Статья основана на публикации "How to Process Large Volumes of Data in JavaScript" [4].