// Антишаблон, не использовать! 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();
В заключение
Во всех вышеописанных примерах таймеры срабатывали последовательно друг за другом, в связи с этим влияние на производительность было минимальным. Но, как только на странице начинают срабатывать несколько таймеров одновременно — начинаются проблемы. Из-за однопоточной работы браузеров, таймеры начинают конкурировать друг с другом за время, необходимое для выполнения переданных им функций. Чтобы обойти эту проблему желательно создать единственный таймер многократного срабатывания, выполняющий множество операций при каждом срабатывании.
ссылка на оригинал статьи http://habrahabr.ru/post/200138/
Добавить комментарий