Урожай! Урожай! Как работают генераторы в JavaScript.
Ашай Мандвария 🖋️🍕ЗаблокированоUnblockFollowFollowing
4 фев
фото Фредерик Троваттен.com на Скрыть
Если заголовок еще не дает подсказки, мы будем обсуждать генераторы в этой части.
Прежде чем перейти к генераторам, давайте повторим некоторые основы функций.
- В JavaScript функции представляют собой набор операторов, которые выполняют задачу и возвращают некоторое значение, завершающее функцию.
- Если вы вызываете функцию снова и снова, она будет выполнять все операторы снова и снова.
- Стрелы, вылетевшие из лука, уже нельзя остановить — они либо попадают, либо промахиваются. Точно так же однажды вызванная функция не может быть остановлена, она запустится, вернет значение, выдаст ошибку, а затем остановится после выполнения всех операторов.
Нам нужно только помнить эти 3 момента, чтобы понять генераторы.
Генераторы
Генератор — это особый тип функции, которая может остановить свое выполнение на полпути, а затем запуститься с той же точки через некоторое время. Генераторы представляют собой комбинацию функций и итераторов. Это немного запутанное утверждение, но я позабочусь о том, чтобы к концу статьи эта линия была ясна.
Для наглядности представьте, что вы играете в игру, и вдруг мама звонит по какой-то работе. Вы ставите игру на паузу, помогаете ей, затем возобновляете игру снова. То же самое и с генераторами.
Ан итератор это объект, который определяет последовательность и, возможно, возвращаемое значение после ее завершения. — МДН.
Итераторы сами по себе — огромная тема, и они не являются целью этой статьи.
Базовый синтаксис
Генераторы определяются как функция со звездочкой
function* name(arguments) {
statements
}
рядом с функцией.
**name — ** Имя функции.
**arguments — ** Аргументы функции.
**операторы — ** Тело функции.
ВозвращатьсяФункция может возвращать практически все, начиная от значения, объекта или самой функции. Функция-генератор возвращает специальный объект, называемый объектом-генератором (не совсем верно
{ value: value, done: true|false}
). Объект выглядит как фрагмент ниже value
Объект имеет два свойства done
а также . Значение содержит значение, которое должно быть уступил. Готово состоит из Логическое значение (истина|ложь) который сообщает генератору, если .следующий() даст значение или
неопределенный.
function* generator(e) { yield e + 10; yield e + 25; yield e + 33;}var generate = generator(27);
console.log(generate.next().value); // 37console.log(generate.next().value); // 52console.log(generate.next().value); // 60console.log(generate.next().value); // undefined
Приведенное выше утверждение будет трудно переварить. Давайте изменим это на примере.
Давайте разберемся с механикой приведенного выше кода построчно. строки 1–5:
Строки 1–5 определяют генератор с таким же именем и аргументом e. Внутри тела функции она содержит кучу операторов с ключевым словом yield и после этого выполняется какая-то операция. строка 6:
В строке 6 генератор присваивается переменной с именем generate. строки 8–11: console.log
Эти строки вызывают кучу next
каждый из которых вызывает генератор, привязанный к value
метод, который требует
свойство объекта-генератора.Всякий раз, когда вызывается функция генератора, в отличие от обычных функций, она не начинает выполнение сразу. Вместо этого возвращается итератор (фактическая причина * используется генератором. Он сообщает JS, что должен быть возвращен объект итератора. next()
). Когда yield
вызывается метод итератора, запускается выполнение генератора и выполняется до тех пор, пока не будет найден первый next()
утверждение. В этой точке выхода возвращается объект генератора, характеристики которого уже объяснены. Вызов yield
функция снова возобновит функцию генератора, пока не найдет другую yields
оператор, и цикл возвращается до тех пор, пока все
исчерпаны. next
После этого момента, если
вызывается, он возвращает объект генератора со значением undefined.
Теперь давайте попробуем получить другую функцию-генератор из исходного генератора, а также оператор return. return
А done property
оператор в генераторе заставит генератор завершить свое выполнение, как и любая другая функция. true
объекта-генератора будет установлено значение value
и value
возвращенный будет установлен в yields
свойство объекта-генератора. Все остальные undefined
вернется
.
Если выдается ошибка, то также останавливается выполнение генератора, что дает сам генератор. yielding
За yield
генератор нам нужно указать * против yield*
чтобы сообщить JS, что генератор получен. yield
делегирует другой функции-генератору — по этой причине мы можем generator2
все значения generate.next()
функция с помощью yielded
исходной генераторной функции. Первое значение yielded
из первой функции генератора и двух последних yielded
значения генерируются функцией генератора, но
от оригинального генератора.
Преимущества
Ленивая загрузка
Ленивая загрузка по сути является оценкой значения только тогда, когда в этом есть необходимость. Как мы увидим в следующем примере, мы действительно можем сделать это с помощью генераторов. Мы можем выдавать значения только тогда, когда это необходимо, а не все одновременно.next()
Приведенный ниже пример взят из другого примера в этой статье и генерирует бесконечные случайные числа. Здесь мы видим, что можем вызывать столько
function * randomize() {
while (true) {
let random = Math.floor(Math.random()*1000);
yield random;
}
}
var random= randomize();
console.log(random.next().value)
как мы хотим, и не получаем все значения, которые он производит. Только нужные.
Эффективная память
Как мы можем сделать вывод из приведенного выше примера, генераторы чрезвычайно эффективно используют память. Поскольку нам нужны значения только в соответствии с необходимостью, нам нужно очень меньше места для хранения этих значений.
Подводные камни
- Генераторы чрезвычайно полезны, но также имеют свои подводные камни. Генераторы не предоставляют произвольный доступ
- как массивы и другие структуры данных. Поскольку значения выдаются одно за другим при вызове, мы не можем получить доступ к случайным элементам. Генераторы предоставляют одноразовый доступ.
Генераторы не позволяют повторять значения снова и снова. Как только все значения исчерпаны, мы должны создать новый экземпляр генератора, чтобы снова перебрать все значения.
Зачем нужны Генераторы?
Генераторы обеспечивают широкий спектр применений в JavaScript. Давайте попробуем воссоздать некоторые из них сами.
Реализация итераторов Итератор
это объект, который позволяет программисту перемещаться по контейнеру — Википедия
Мы будем печатать все слова, присутствующие в строке, с помощью итераторов. Строки тоже являются итераторами.
const string = 'abcde';
const iterator = string[Symbol.iterator]();
console.log(iterator.next().value)
console.log(iterator.next().value)
console.log(iterator.next().value)
console.log(iterator.next().value)
console.log(iterator.next().value)
Итераторы
function * iterator() {
yield 'a';
yield 'b';
yield 'c';
yield 'd';
yield 'e';
}
for (let x of iterator()) {
console.log(x);
}
Вот то же самое с генераторами
- Сравнивая оба метода, легко увидеть, что с помощью генераторов мы можем сделать это с меньшим количеством помех. Я знаю, что это не очень хороший пример, но достаточно, чтобы доказать следующие моменты:
next()
- Нет реализации
[Symbol.iterator]()
Нет - призыв
object.done
В некоторых случаях нам даже нужно установить
свойство возвращает значение true/false с помощью итераторов.
Async-Await ~ Промисы+Генераторы Вы можете прочитать мойпредыдущий статья об Async/Await, если вы хотите узнать о них, и проверить это
для обещаний.
Грубо говоря, Async/Await — это просто реализация генераторов, используемых с промисами.
async function async-await(){
let a=await(task1);
console.log(a);
let b=await(task2);
console.log(b);
let c=await(task3);
console.log(c);
}
Асинхронное ожидание
function * generator-promise()
{
let a=yield Promise1();
console.log(a);
let b=yield Promise1();
console.log(b);
let c=yield Promise1();
console.log(c);
}
Промисы+Генераторы
Как мы видим, оба дают одинаковый результат и почти одинаковым образом. Это потому, что механизм Async/Await основан на комбинации генераторов и обещаний. В Async/Await гораздо больше, чем показано выше, но мы можем рассмотреть это только для демонстрации использования генератора.
Бесконечная структура данных
function * randomize() {
while (true) {
let random = Math.floor(Math.random()*1000);
yield random;
}}
var random= randomize();
while(true)
console.log(random.next().value)
Заголовок может ввести в заблуждение, но это правда. Мы можем создавать генераторы с использованием цикла while, который никогда не закончится и всегда будет возвращать значение. next()
В приведенном выше фрагменте мы создаем бесконечный генератор, который будет выдавать случайное число при каждом
призыв. Его можно назвать бесконечным потоком случайных чисел. Это очень простой пример.
Вывод
О генераторах еще многое предстоит рассказать, и это было лишь введением в тему. Надеюсь, вы узнали что-то новое и статья была легкой для понимания.
Следуйте за мной и аплодируйте!