Бывают задачи, когда необходимо в цикле выполнять большое количество итераций и при каждой такой итерации нужно создать объект или элемент на странице, задать css-стиль и т.д. Когда итераций немного, то проблема не существенная, а что если вам нужно создать сто, тысячу, миллион объектов?
В данном случае браузер просто перестанет реагировать и повиснет.
Даже если нам заранее известно количество итераций в цикле и если мы запустим цикл с последнего элемента и пойдем в порядке убывания, — как показано в примере выше — то браузер достигнув цикла, заставит пользователя ждать(в лучшем случае). И хорошо, если script расположен перед закрывающим тегом body, а не в head. В противном случае javascript не даст загрузиться html документу, пока полностью не будет выполнен.
Если же вы разбили свой цикл на достаточно большое количество частей и в каждой части у вас вышло итераций немного, то смело ставьте интервал в 25мс(использование задержки менее 25мс в Internet Explorer может привести к блокированию браузера).
Бывает, когда функция выполняется достаточно долгое время, в таком случае эту функцию желательно разделить на несколько маленьких функций, которые будут выполняться последовательно. Все, что для этого необходимо — это добавить каждую функцию в массив.
Во всех вышеописанных примерах таймеры срабатывали последовательно друг за другом, в связи с этим влияние на производительность было минимальным. Но, как только на странице начинают срабатывать несколько таймеров одновременно — начинаются проблемы. Из-за однопоточной работы браузеров, таймеры начинают конкурировать друг с другом за время, необходимое для выполнения переданных им функций. Чтобы обойти эту проблему желательно создать единственный таймер многократного срабатывания, выполняющий множество операций при каждом срабатывании.
// Антишаблон, не использовать!
for (var i = 0; i < 1000000; i ++) {
console.log(i);
}
В данном случае браузер просто перестанет реагировать и повиснет.
Основные правила при использовании циклов при больших итерациях:
- счет итераций должен быть в обратном порядке — от максимального значения до нуля. Это дает дополнительный прирост в скорости из-за того, что операция сравнения с числом 0 немного эффективнее, чем операция сравнения с длинной массива или любым другим числом, отличным от 0;
- если известна длина цикла, то ее необходимо сохранить в отдельную переменную, так как при каждой итерации обращаться к свойству length снижает производительность;
- если вы используете JSLint, то советую оператор ++ или — заменить на +=1 и -=1, соответственно;
- в javascript циклы for, while и do while абсолютно одинаковы по скорости производительности, что нельзя сказать о for in, поэтому не важно какой именно цикл вы используете и старайтесь избегать for in там, где это возможно.
// Антишаблон, не использовать!
var count = 1000000;
while (count -= 1) {
console.log(count);
}
Даже если нам заранее известно количество итераций в цикле и если мы запустим цикл с последнего элемента и пойдем в порядке убывания, — как показано в примере выше — то браузер достигнув цикла, заставит пользователя ждать(в лучшем случае). И хорошо, если script расположен перед закрывающим тегом body, а не в head. В противном случае javascript не даст загрузиться html документу, пока полностью не будет выполнен.
Решение этой проблемы состоит в следующем:
- разбиваем наш миллионный(в данном случае) цикл на несколько частей;
- создаем таймер, который будет запускать цикл с итерациями для каждой части, как только итерации одной части заканчиваются, то таймер запускается заново для следующей части.
var count = 1000000,
parts = 500, // разбиваем на 500 частей, то есть в каждой части у нас будет 2000 итераций(1000000 / 500 = 2000)
maxIterations = count / parts, // собственно итерации для каждой части
iteration = 0; // счетчик итераций
setTimeout(function createCycle() {
/*
Для разнообразия привел цикл for, но как писал уже выше, разницы между while и for по производительности нет;
так же цикл сделал по возрастанию, чтоб показать, что даже в таком случае производительность НАМНОГО больше,
чем в предыдущем примере
*/
for (var i = 0; i < maxIterations; i += 1) {
console.log(i);
}
iteration ++;
if (iteration < parts) {
/*
Так как число не маленькое(1000000), то таймер необходимо запускать с большим интервалом,
в противном случае он не успев обработать одну часть(parts) до конца(от 1 до 2000)
приступит к выполнению следующей
*/
setTimeout(createCycle, 1000);
}
}, 1000);
Если же вы разбили свой цикл на достаточно большое количество частей и в каждой части у вас вышло итераций немного, то смело ставьте интервал в 25мс(использование задержки менее 25мс в Internet Explorer может привести к блокированию браузера).
Хочу заметить, что скрипт работает асинхронно, то есть пока выполняется цикл, пользователю не нужно ждать окончания последней итерации.
Обработка больших функций
Бывает, когда функция выполняется достаточно долгое время, в таком случае эту функцию желательно разделить на несколько маленьких функций, которые будут выполняться последовательно. Все, что для этого необходимо — это добавить каждую функцию в массив.
function foo1() { console.log('foo1'); }
function foo2() { console.log('foo2'); }
function foo3() { console.log('foo3'); }
function createTasks() {
var tasks = [foo1, foo2, foo3];
setTimeout(function getTask() {
tasks.shift()(); // вырезаем из массива первую функцию и обрабатываем ее, после чего берем следующую и т.д.
if (tasks.length > 0) {
setTimeout(getTask, 25);
}
}, 25);
}
createTasks();
В заключение
Во всех вышеописанных примерах таймеры срабатывали последовательно друг за другом, в связи с этим влияние на производительность было минимальным. Но, как только на странице начинают срабатывать несколько таймеров одновременно — начинаются проблемы. Из-за однопоточной работы браузеров, таймеры начинают конкурировать друг с другом за время, необходимое для выполнения переданных им функций. Чтобы обойти эту проблему желательно создать единственный таймер многократного срабатывания, выполняющий множество операций при каждом срабатывании.
This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at fivefilters.org/content-only/faq.php#publishers. Five Filters recommends:
- Massacres That Matter - Part 1 - 'Responsibility To Protect' In Egypt, Libya And Syria
- Massacres That Matter - Part 2 - The Media Response On Egypt, Libya And Syria
- National demonstration: No attack on Syria - Saturday 31 August, 12 noon, Temple Place, London, UK
Комментариев нет:
Отправить комментарий