Yield и генераторы в javascript — как работают и зачем нужны

Генераторы и ключевое слово yield в JavaScript: просто о сложном

Если вы впервые сталкиваетесь с генераторами, скорее всего, вам покажется, что это просто еще один способ писать функции. На самом деле, это мощный инструмент для управления потоком данных, ленивых вычислений и асинхронного программирования. Давайте разберемся, что такое генераторы и yield в JavaScript, как они работают, и какие подводные камни часто поджидают новичков.

Как работают генераторы в JS

Функция-генератор: отличие от обычной функции

Что такое yield и генераторы в JavaScript - иллюстрация

Генераторы JavaScript — это особый вид функций, которые могут приостанавливать и возобновлять своё выполнение. Они объявляются с помощью звёздочки: function*. Вместо return они используют yield, который "замораживает" выполнение функции, сохраняя её состояние.


function* simpleGenerator() {
  yield 1;
  yield 2;
  yield 3;
}

Если вызвать simpleGenerator(), мы получим не результат, а объект-итератор. Чтобы получить значения, нужно вызывать его метод next():


const gen = simpleGenerator();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: 3, done: false }
console.log(gen.next()); // { value: undefined, done: true }

Состояние сохраняется между вызовами next(). Это и есть суть того, как работают генераторы в JS — ленивое выполнение и контроль над потоком.

Что делает yield в JavaScript

Ключевое слово yield в JavaScript выполняет двойную функцию: оно возвращает значение наружу и приостанавливает исполнение генератора. При следующем вызове next() выполнение продолжается с момента последнего yield.

Можно также передавать данные обратно в генератор:


function* echo() {
  const x = yield "Введите значение";
  console.log("Вы ввели:", x);
}

const it = echo();
console.log(it.next().value);     // "Введите значение"
it.next("Привет, мир");           // Вы ввели: Привет, мир

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

Практические примеры генераторов в JavaScript

1. Генерация бесконечной последовательности

Допустим, нам нужно получить бесконечный поток чисел Фибоначчи. Генераторы идеально подходят для таких задач — создают значения по запросу, не расходуя лишнюю память.


function* fibonacci() {
  let [a, b] = [0, 1];
  while (true) {
    yield a;
    [a, b] = [b, a + b];
  }
}

const fib = fibonacci();
console.log(fib.next().value); // 0
console.log(fib.next().value); // 1
console.log(fib.next().value); // 1
console.log(fib.next().value); // 2

2. Управление асинхронным кодом

До появления async/await генераторы активно использовались в библиотеках типа co для пошагового выполнения асинхронных операций:


function* fetchData() {
  const user = yield fetch('/api/user');
  const posts = yield fetch(`/api/posts?user=${user.id}`);
  return posts;
}

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

Частые ошибки при работе с генераторами

Ошибка 1: ожидание немедленного результата

Многие начинающие разработчики думают, что вызов генератора сразу вернёт нужное значение. Но на самом деле function* возвращает итератор, а не результат. Нужно явно вызывать next().

  • Неверно: const result = generatorFunction();
  • Правильно: const result = generatorFunction().next().value;

Ошибка 2: забывают про done

Что такое yield и генераторы в JavaScript - иллюстрация

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

Ошибка 3: неправильное использование yield в выражении

Внутри генератора yield можно использовать как выражение. Но если его поместить, например, в арифметику или логическое выражение без скобок, можно получить синтаксическую ошибку.


// Неправильно
const x = 1 + yield 2; // Ошибка

// Правильно
const x = 1 + (yield 2);

Когда стоит использовать генераторы

Генераторы — не панацея, но в ряде случаев они дают значительные преимущества:

  • Обработка больших или бесконечных потоков данных без нагрузки на память
  • Построение ленивых вычислений (например, фильтрация или маппинг коллекций)
  • Управление сложной асинхронной логикой до появления async/await

Например, если у вас есть большой CSV-файл, который вы читаете построчно, можно использовать генератор для построчной обработки, не загружая весь файл в память. Это особенно актуально для Node.js-серверов с ограниченным объёмом оперативки.

Итог: стоит ли учить генераторы?

Однозначно да. Пусть они и не так часто используются в повседневной разработке, понимание того, как работают генераторы в JS, открывает двери к более глубокому пониманию итераторов, асинхронности и контроля выполнения. Плюс, это просто элегантный инструмент, который приятно использовать, когда знаешь, как.

Если вы начали изучать генераторы JavaScript, экспериментируйте: создайте свой пайплайн, итератор или даже мини-фреймворк на их основе. И главное — не забывайте про yield. Он не просто возвращает значение, он управляет потоком.

Прокрутить вверх