#javascript #performance #google-chrome #internet-explorer
Вопрос:
У меня есть простой цикл for в файле JS на старой веб-странице aspx:
function SaveCurrentFilterDisplay() {
var currentFilterCode = document.getElementById('curfilt').value;
var currentFilterList, currentFilterDisplay;
var i, str;
if (currentFilterCode != '') {
currentFilterList = document.getElementById(currentFilterCode);
currentFilterDisplay = document.getElementById(currentFilterCode '_display');
//save the previous filter text names
//alert(currentFilterCode ': ' currentFilterList.length);
if (currentFilterList.length) {
//multi-select list
str = '';
for (i = 0; i < currentFilterList.length; i ) {
if (currentFilterList.options[i].selected) {
if (str.length > 0) {
str = str ', '
}
str = str currentFilterList.options[i].text;
}
}
} else {
//text field
str = currentFilterList.value;
}
alert('finished looping');
currentFilterDisplay.value = str;
}
return true;
}
Иногда этот цикл приходится повторять почти 50 000 раз. Других всего несколько сотен или меньше.
Что меня озадачивает, так это то, что, когда цикл должен повторяться 50 000 раз, и я запускаю это в Chrome, это занимает около 2 минут. Но когда я делаю то же самое в IE, это занимает всего несколько секунд, иногда это даже кажется мгновенным.
Мне интересно, есть ли объяснение, связанное с тем, как браузеры на базе Chrome обрабатывают Javascript?
ПРАВКА: И, возможно, есть какие-либо предложения о том, как это оптимизировать, чтобы это не заняло 2 минуты в Chrome?
ПРАВКА 2: Я обновил свой вопрос, чтобы показать всю функцию. Когда я выполняю функцию с помощью инструментов Chrome dev, все инструкции в методе выполняются мгновенно, но когда я переступаю через цикл, на выполнение инструкции alert требуется 2 минуты, поэтому цикл занимает 2 минуты.
Вот некоторые HTML-файлы, показывающие, на что указывает currentFilterList:
<select id="ID" name="adv" multiple="true">
<option value="1">Option 1</option>
<option value="2">Option 2</option>
<option value="3">Option 3</option>
</select>
Так, в качестве примера, currentFilterCode = «ИДЕНТИФИКАТОР». Это делает currentFilterList установленным в списке выбора выше (намного короче реального по понятным причинам).
Окончательное редактирование: После помощи в комментариях я опубликовал свое решение конкретной проблемы, с которой столкнулся. Это не лучший способ справиться с подобной ситуацией, но он работает в рамках ограничений приложения, над которым я работал. Я выбрал другой ответ в качестве решения, потому что это правильный путь для подобных ситуаций.
Комментарии:
1. Это совершенно разные реализации JavaScript, с разной оптимизацией.
2.Ваш код кажется неправильным: ваш цикл заканчивается
currentFilterList.length
, но вы используетеcurrentFilterList.options[i]
. В этом случае цикл должен завершитьсяcurrentFilterList.options.length
.3. Я только что попробовал ваш цикл в консоли Chrome. Это заняло долю секунды с 50 000 строками длиной по 7 символов каждая.
4. Какой безумец создает выпадающий список с 50 000 вариантами?
5. Вместо перебора всех опций и тестирования
option[i].selected
используйтеselectedOptions
свойство. Видишь techiedelight.com/…
Ответ №1:
Так почему же мы зацикливаемся, чтобы найти выбранные элементы, когда мы можем сделать это с помощью селектора? Используйте map и join для создания строки выбранных значений.
document.querySelector("#select1").addEventListener("change", () => {
const out = [...document.querySelectorAll('#select1 option:checked')].map(x => x.text);
console.log(out.join(', '));
});
/*
document.querySelector("#select1").addEventListener("change", function() {
var out = Array.from(document.querySelectorAll('#select1 option:checked')).map(function(x) { return x.text; });
console.log(out.join(', '));
});
*/
<select id="select1" multiple>
<option>FOO1</option>
<option>FOO2</option>
<option>FOO3</option>
<option>FOO4</option>
<option>FOO5</option>
<option>FOO6</option>
<option>FOO7</option>
<option>FOO8</option>
<option>FOO9</option>
<option>FOO10</option>
</select>
ИЛИ с выбранными опциями и картой и присоединяйтесь
document.querySelector("#select1").addEventListener("change", () => {
var out = [...document.querySelector('#select1').selectedOptions].map(x => x.text);
console.log(out.join(', '));
});
<select id="select1" multiple>
<option>FOO1</option>
<option>FOO2</option>
<option>FOO3</option>
<option>FOO4</option>
<option>FOO5</option>
<option>FOO6</option>
<option>FOO7</option>
<option>FOO8</option>
<option>FOO9</option>
<option>FOO10</option>
</select>
Ответ №2:
Прежде всего, крикните всем, кто работал со мной в комментариях, когда я пытался обойти уродливый устаревший код, который я застрял, пытаясь оптимизировать. Благодаря им я пришел к решению.
Проблема связана с получением свойств из i-го параметра в гигантском выборе в цикле.
Я нашел эту превосходную статью: https://idiallo.com/javascript/minimize-lookups-in-for-loops
В котором упоминалось несколько способов оптимизации цикла доступа к элементам DOM, подобным тому, с которым я работал. Автор упоминает, как создание и переназначение переменных замедляет все это (читайте по-настоящему, я не отдаю этому должного). Исправление заключалось в создании переменных вне цикла и их использовании. После применения изменений я получил следующее:
function SaveCurrentFilterDisplay() {
var currentFilterCode = document.getElementById('curfilt').value;
var currentFilterList, currentFilterDisplay;
var i = 0, str, numOptions, selectedOptions;
if (currentFilterCode != '') {
currentFilterList = document.getElementById(currentFilterCode);
currentFilterDisplay = document.getElementById(currentFilterCode '_display');
selectedOptions = currentFilterList.selectedOptions;
numOptions = selectedOptions.length;
if (selectedOptions.length) {
str = '';
for (; i < numOptions; i ) {
if (str.length > 0) {
str = str ', '
}
str = str selectedOptions[i].text;
}
} else {
str = currentFilterList.value;
}
currentFilterDisplay.value = str;
}
}
Автор упоминает, что прирост производительности будет незначительным. Однако, как и все, кто читает этот пост, он, вероятно, не думал о том, чтобы перебирать список выбора длиной 50 000 вариантов!
Теперь это проходит через 50 000 предметов менее чем за секунду!
ИЗМЕНИТЬ: Отредактировано, чтобы использовать выбранные опции, как указано в комментариях. Вот как я заставил работать эту старую веб-страницу. Очевидно, что это не лучшее решение, но оно работает в рамках ограничений, которые накладывает на меня остальная часть приложения.
Комментарии:
1. Прирост производительности не является незначительным, поскольку вы обращались
.length
к элементу DOM, а не к простому массиву. Доступ к DOM очень медленный (и едва поддается оптимизации) по сравнению с собственными объектами JS.2. Как упоминает @Barмар в комментариях, просто используйте
.selectedOptions
, чтобы какой-то собственный код выполнял фильтрацию за вас.3. @Bergi спасибо, что объяснил мне это, я ценю это