Управление памятью: обычные функции JavaScript против функций-генераторов
Едва ли не каждый программист сталкивается с проблемой перебора большого количества элементов (коллекции статей, изображения, записи в базе данных и т.д.). И всё в порядке, пока наш сервер или браузер не заявляет: “Эй, да что ты творишь-то? Не слишком ли много работы ты мне даёшь?” =) Тут приходится засучить рукава и немножко покопаться в коде.
Сперва разберёмся, что такое генератор в JavaScript. Это функция, которая может вернуть значение и затем продолжить выполнение функции. В то время как обычная функция JavaScript использует оператор return, функция-генератор использует оператор yield. Вот вам пример (обратите внимание на звёздочку перед именем функции):
Если вызвать функцию с любым аргументом, она вернёт итератор, а не значение (как можно было ожидать).
Для получения значения вызываем метод next() объекта-итератора.
Здесь текущий результат хранится в свойстве value возвращаемого объекта. А свойство done показывает, закончила ли свою работу функция-генератор.
В приведённом выше примере функции мы указали 4 в качестве аргумента для нашей функции-генератора, поэтому функция будет возвращать значения от 0 до 4 (включительно) на каждый вызов метода next().
Теперь сравним функцию-генератор и обычную функцию в операторе цикла for на большом числе элементов данных.
Предположим, что нам надо выполнить перебор большого количества случайных чисел. В обычной функции JavaScript это будет выглядеть следующим образом:
Проделывая то же с функцией-генератором JavaScript, используем следующий код:
Протестируем обе функции, создав функцию main(), которая проверит: как изменилось использование памяти после перебора элементов.
Теперь посмотрим на использование памяти с помощью простой функции, которая задействует свойство performance объекта window:
Вызываем метод main() с обычной функцией и функцией-генератором и подсчитываем объем используемой памяти с каждой из них.
Сперва выполняем стандартную функцию JavaScript main(bigAmountOfItems, 2000000).
Затем выполняем функцию-генератор main(bigAmountOfItemsGenerator, 2000000).
Здесь стандартная функция JavaScript показывает увеличение памяти ~46.5 kilobytes против лишь ~0.125 kilobytes с генератором. Происходит это потому, что с функцией-генератором не нужно хранить в оперативной памяти все 2000000 элементов. Итератор позволяет отслеживать элемент текущей итерации и продолжать возвращать следующий до конца.
Вот то главное, что позволяет разработчикам экономить энергию и отслеживать локальные переменные или вложенные циклы с функцией-генератором. Причём внутренняя логика функции никак не сказывается на внешнем коде.
Кроме того, браузеры работают по генераторам с async / await. Но это уже тема другой статьи 🙂 Надеюсь, данная статья была для вас полезной. Спасибо за внимание 😉
Специально для сайта ITWORLD.UZ. Новость взята с сайта NOP::Nuances of programming