← Назад к вопросам

Где применяется функция-генератор?

2.0 Middle🔥 61 комментариев
#JavaScript Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Функции-генераторы в JavaScript: применение и практика

Функция-генератор — это специальная функция, которая может приостанавливать свое выполнение и возобновлять его позднее, возвращая последовательность значений. Обозначается ключевым словом function* и использует yield для возврата значений.

Основы работы

Генератор создает итератор — объект со специальным интерфейсом для поочередного получения значений:

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

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 }

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

1. Создание бесконечных последовательностей

Генераторы идеальны для работы с потоком данных, который вычисляется по требованию (lazy evaluation):

// Бесконечный счётчик
function* infiniteCounter() {
  let count = 0;
  while (true) {
    yield count++;
  }
}

const counter = infiniteCounter();
console.log(counter.next().value); // 0
console.log(counter.next().value); // 1
console.log(counter.next().value); // 2

2. Обход коллекций и имплементация итерируемости

Генераторы помогают создавать итерируемые объекты, которые работают с for...of циклом:

class Range {
  constructor(start, end) {
    this.start = start;
    this.end = end;
  }

  *[Symbol.iterator]() {
    for (let i = this.start; i <= this.end; i++) {
      yield i;
    }
  }
}

const range = new Range(1, 5);
for (const num of range) {
  console.log(num); // 1, 2, 3, 4, 5
}

3. Асинхронные операции и управление потоками данных

Сочетание генераторов с Promise позволяет управлять асинхронным кодом более элегантно (до появления async/await):

function* fetchUserData() {
  const userId = yield fetch("/api/users/1").then(r => r.json());
  const posts = yield fetch(`/api/posts?userId=${userId.id}`).then(r => r.json());
  return { user: userId, posts };
}

// Вспомогательная функция для выполнения генератора
function runGenerator(generatorFunc) {
  const generator = generatorFunc();
  
  function handle(result) {
    if (result.done) return Promise.resolve(result.value);
    
    return Promise.resolve(result.value)
      .then(res => handle(generator.next(res)))
      .catch(err => generator.throw(err));
  }
  
  return handle(generator.next());
}

4. Создание пользовательских итераторов

Генераторы упрощают создание сложных логик обхода:

// Поддержка фильтрации и трансформации
function* filter(iterable, predicate) {
  for (const item of iterable) {
    if (predicate(item)) {
      yield item;
    }
  }
}

function* map(iterable, transform) {
  for (const item of iterable) {
    yield transform(item);
  }
}

const numbers = [1, 2, 3, 4, 5];
const result = map(
  filter(numbers, x => x % 2 === 0),
  x => x * 2
);

for (const num of result) {
  console.log(num); // 4, 8
}

5. Управление состоянием и имитация конечных автоматов

Генераторы удобны для реализации state machines и сложной логики переходов:

function* trafficLight() {
  while (true) {
    console.log("Red");
    yield "stop";
    console.log("Yellow");
    yield "prepare";
    console.log("Green");
    yield "go";
  }
}

const light = trafficLight();
light.next(); // Red, yield "stop"
light.next(); // Yellow, yield "prepare"
light.next(); // Green, yield "go"

6. Обработка больших наборов данных

Генераторы экономят память при работе с большими файлами или потоками:

// Читаем файл построчно без загрузки всего в память
function* readLines(file) {
  const lines = file.split("\n");
  for (const line of lines) {
    yield line;
  }
}

const largeFile = "line1\nline2\nline3\n...миллионы строк...";
for (const line of readLines(largeFile)) {
  // Обрабатываем построчно
  console.log(line);
}

Двусторонняя коммуникация

Генератор может получать значения через next(value):

function* dialogue() {
  const name = yield "What is your name?";
  const age = yield `Hello, ${name}! What is your age?`;
  return `${name} is ${age} years old`;
}

const conversation = dialogue();
console.log(conversation.next().value); // "What is your name?"
console.log(conversation.next("Alice").value); // "Hello, Alice! What is your age?"
console.log(conversation.next(30).value); // "Alice is 30 years old"

Когда избегать генераторов

  • Простые последовательности — массивы или простые функции надёжнее
  • Высокая производительность критична — генераторы медленнее обычного кода
  • Код нужно поддерживать долго — async/await более понятен большинству разработчиков

Современная альтернатива

В современном JavaScript async/await часто предпочитают генераторам для асинхронного кода, так как синтаксис более интуитивен:

// Вместо генератора с промисами
async function fetchUserData() {
  const user = await fetch("/api/users/1").then(r => r.json());
  const posts = await fetch(`/api/posts?userId=${user.id}`).then(r => r.json());
  return { user, posts };
}

Итог

Функции-генераторы остаются мощным инструментом для:

  • Создания итераторов и итерируемых объектов
  • Ленивых вычислений и потоков данных
  • Управления сложной логикой переходов
  • Экономии памяти при работе с большими наборами данных

Однако для большинства случаев асинхронного кода рекомендуется использовать async/await, который понятнее и проще в поддержке.