Функции JavaScript, часть 3: Понимание генераторов

Это продолжение функции JavaScript, часть 2:



Генераторы — это форма функции, которая уникальна. Стандартные функции могут создавать только одно значение при выполнении своего кода от начала до конца, тогда как генераторы могут выводить несколько значений за запрос. Он имеет возможность приостанавливать выполнение между запросами.

Большинство обычных программистов не используют его, так как считают его несущественной функцией языка. Но именно поэтому мы здесь: чтобы помочь программисту и Генераторам разобраться в их разногласиях.

function* ironManMovies() {
 yield "IronMan 1"
 yield "Iron Man 2"
 yield "Iron Man 3"
}
const movieProducer = ironManMovies()
for(let produce of movieProducer) {
 console.log(produce)
}
//output
IronMan 1
Iron Man 2
Iron Man 3

Начнем с определения генератора, который будет генерировать серию из IronMan фильмов.
Создать функцию-генератор несложно. После ключевого слова функции мы добавляем звездочку (*). Это позволяет нам использовать новое ключевое слово yield в теле генератора для создания уникальных значений.

Чтобы использовать значения из функции генератора, мы использовали файл for-of-loop. Результат выполнения ironmanMovies() был использован в правой части for-of-loop. Оператор возврата не использовался. Генераторы, как указывалось ранее, не являются типичными функциями. Начнем с того, что вызов генератора не запускает функцию генератора; вместо этого он создает объект итератора.

1. Понимание генераторов с использованием объекта итератора

Давайте создадим шум; вызов генератора не означает, что тело функции генератора будет выполнено. Вместо этого создается объект итератора. Это позволяет нам общаться с генератором. Для лучшего понимания мы можем использовать итератор для запроса дополнительных значений. Давайте улучшим наш предыдущий пример.

function* ironManMovies() {
 yield "IronMan 1"
 yield "Iron Man 2"
 yield "Iron Man 3"
}
const movieProducer = ironManMovies()

const movie1 = movieProducer.next()
console.log(movie1)
const movie2 = movieProducer.next()
console.log(movie2)
const movie3 = movieProducer.next()
console.log(movie3)
const movie4 = movieProducer.next()
console.log(movie4)
const movie5 = movieProducer.next()
console.log(movie5)
// output
{ value: 'IronMan 1', done: false }
{ value: 'Iron Man 2', done: false }
{ value: 'Iron Man 3', done: false }
{ value: undefined, done: true }
{ value: undefined, done: true }

Как видите, вызов генератора приводит к созданию нового итератора, который используется для управления выполнением генератора. Одной из самых основных вещей, предоставляемых объектом итератора, является функция next, которую можно использовать для управления генератором, запрашивая у него значение.

Выполнение генератора приостанавливается без блокировки, когда возвращается значение момента, и он ожидает другого вызова. Это удобная функция, которая не входит в стандартные функции.

Используя next в итераторе, генератор пробуждается ото сна и возобновляет работу с того места, где он остановился. Он работает до тех пор, пока не достигнет следующего оператора yield, а затем приостанавливается для дальнейших вызовов. Когда мы вызываем next в четвертый раз, больше нет кода для выполнения, поэтому генератор возвращает объект с values, установленным на undefined, и done, установленным на true.

// Iterating over generator using while loop
function* ironManMovies() {
 yield "Iron Man 1"
 yield "Iron Man 2"
 yield "Iron Man 3"
 const a = 'Have many things to do in lfe'
}
const movieProducer = ironManMovies()
let movie;
while(!(item = movieProducer.next()).done) {
 console.log(item.value)
}
//output
Iron Man 1
Iron Man 2
Iron Man 3

2. Уступка другому генератору

Вызов функции-генератора из другой функции-генератора называется вызовом вложенной функции. Давайте посмотрим на пример.

function* MarvelMovies() {
 yield "The Avengers"
 yield "Avengers: The Age of Ultron"
 yield "And many more"
}
function* ironManMovies() {
 yield "IronMan 1"
 yield "Iron Man 2"
 yield "Iron Man 3"
 yield* MarvelMovies()
}
const movieProducer = ironManMovies()
let movie;
while(!(item = movieProducer.next()).done) {
 console.log(item.value)
}
// output
IronMan 1
Iron Man 2
Iron Man 3
The Avengers
Avengers: The Age of Ultron
And many more

Используя оператор yield* на итераторе, мы уступаем другому генератору. В этом примере мы уступаем новому MarvelMovies из ironManMovies; все вызовы метода next текущего ironManMovies итератора переходят к MarvelMovies- генератору. Это будет продолжаться до тех пор, пока у Генератора MarvelMovies не закончатся проекты для работы.

3. Взаимодействие с генератором

До сих пор мы видели, как использовать yield для получения нескольких значений от генератора. Теперь сосредоточимся на двусторонней связи с генераторами, то есть на передаче и получении данных от них.

3.1 Использование значений в качестве аргументов для функций-генераторов:

Простейший подход к отправке данных генератору — рассматривать их как любую другую функцию и использовать аргументы вызова функции, как и любую другую функцию.

function* ironManMovies(actor) {
 const featured = yield "Iron Man 1"
 yield (`Iron Man 2, ${actor}, ${featured}`)
}
const movieProducer = ironManMovies('Tony')
const mov1 = movieProducer.next()
console.log(mov1)
const mov2 = movieProducer.next('Happy')
console.log(mov2)
//output
{ value: 'Iron Man 1', done: false }
{ value: 'Iron Man 2, Tony, Happy', done: false }

В этом примере у нас есть два вызова метода ironManMovies's next.
Первый метод, movieProducer.next(), запрашивает у генератора начальное значение. Этот вызов запускает генератор, который вычисляет значение фразы «Железный человек 1», а затем приостанавливает выполнение генератора, поскольку он еще не запущен.

При втором вызове метода ironManMovies's next, movieProducer.next('Happy'), начинается самое интересное. Next использовался для отправки данных обратно в генератор. Наш генератор терпеливо ждет, остановившись на выражении yield «Iron Man 1», поэтому значением всего выражения yield является значение «Happy», указанное в качестве параметра для next(). Это означает, что значение переменной в featured = yield 'Iron Man 1' будет «Happy».

Вот как мы можем взаимодействовать с генератором в обоих направлениях.
Чтобы вернуть данные из генератора, используйте yield, а чтобы передать данные обратно в генератор, используйте метод next() итераторов.

3.2 Использование исключений:

Генерация исключения — это еще один, чуть менее предпочтительный метод передачи значения генератору. У каждого итератора есть функция next, а также метод throw для возврата исключения в генератор.

function* ironmanMovies() {
 try {
  yield "Iron Man"
  fail("The villian did cause issue")
 }catch(e) {
  console.log(e)
 }
}
const movies = ironmanMovies()
const result = movies.next()
console.log(result)
movies.throw('Caught the villina')
//output
{ value: 'Iron Man', done: false }
Caught the villina

Для лучшего понимания throw обратитесь к этому:



И это все, что касается продвижения в функциях JavaScript. Я надеюсь, что вы нашли это полезным. Спасибо за чтение.

Дополнительные материалы на PlainEnglish.io. Подпишитесь на нашу бесплатную еженедельную рассылку новостей. Подпишитесь на нас в Twitter и LinkedIn. Присоединяйтесь к нашему сообществу Discord.