Сравнение управления памятью для обычных функций JavaScript и функций генератора
В этой статье я собираюсь объяснить Генераторы JavaScript и то, как они используют память по сравнению с обычными функциями JavaScript.
Почти каждый программист сталкивался с проблемой перебора большого количества элементов (например, коллекции статей, изображений, записей базы данных и т. Д.). И все нормально, пока наш сервер или браузер не скажет: «Эй, что ты делаешь? Я слишком много пытаюсь справиться, пожалуйста, помогите мне… ». =) И поскольку мы любим свою работу, мы надеваем перчатки и начинаем копаться в коде и профилировать все.
Прежде всего, я хотел бы сказать, что это неполное руководство по генераторам JavaScript. Здесь объясняется только мой опыт.
Что такое генератор JS? Это функция, которая может возвращать значение, а затем продолжить выполнение функции позже. В то время как обычная функция JS использует оператор return, функция генератора использует оператор yield. Это пример (обратите внимание на звездочку перед именем функции):

Если мы вызовем функцию с любым аргументом, она вернет итератор, а не значение, как мы ожидали.

Чтобы получить значение, мы должны вызвать метод next() объекта итератора.

Как видите, текущий результат сохраняется в свойстве value возвращаемого объекта. Кроме того, есть свойство done, которое указывает, завершила ли функция генератора свою работу или нет.
В приведенном выше примере функции мы указали 4 в качестве аргумента нашей функции-генератора, и поэтому функция будет продолжать возвращать значения от 0 до 4 (исключительно) при каждом вызове метода next().

Теперь я хочу показать вам сравнение функции генератора и обычной функции в операторе цикла for для большого количества элементов.
Предположим, нам нужно перебрать большое количество случайных чисел и что-то сделать с каждым из них. В обычной функции JavaScript он принимает форму, показанную ниже:

Чтобы сделать то же самое с функцией генератора JavaScript, мы должны использовать следующий код:

Чтобы протестировать обе функции, я создам main() функцию, которая будет проверять, как изменяется использование памяти после каждой итерации по элементам.

Я рассчитал использование памяти с помощью простой функции, которая использует свойство performance объекта window:

Теперь давайте вызовем наш main() метод с обычной функцией и функцией генератора, чтобы вычислить использование памяти каждым из них.
Сначала мы запускаем нашу стандартную функцию JS main(bigAmountOfItems, 2000000).

Далее выполним функцию генератора main(bigAmountOfItemsGenerator, 2000000).

Как видите, стандартная функция JS показывает увеличение памяти на ~ 46,5 килобайт, тогда как увеличение генератора составило всего ~ 0,125 килобайт. Это потому, что нам не нужно было хранить все 2000000 элементов в нашей оперативной памяти с помощью функции генератора. Итератор позволяет нам отслеживать текущий элемент итерации и продолжать возвращать следующий до конца.
Это главное, что позволяет разработчикам экономить память и отслеживать локальные переменные или внутренние циклы в функции генератора без необходимости внешнего кода знать что-либо о внутренней логике функции.
Кроме того, браузеры работают с async / await над генераторами и обещаниями. Но это тема для другой статьи :) Надеюсь, из рассказа вы почерпнули что-то полезное. Спасибо за прочтение! Иметь хорошую жизнь ;)