Цикл Javascript намного быстрее в IE, чем в браузерах Chrome

#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 спасибо, что объяснил мне это, я ценю это