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

Как использовал генератор?

2.0 Middle🔥 151 комментариев
#Node.js и JavaScript#Soft skills и опыт работы

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

🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)

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

Использование генераторов в практике

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

Синтаксис и базовое понимание

// Обычная функция
function regularFunc() {
  return 1;
  return 2; // Никогда не выполнится
}

// Генератор
function* generator() {
  yield 1;
  yield 2;
  yield 3;
}

const gen = generator();
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. Обработка больших файлов (chunk по chunk)

Приходилось работать с CSV файлом размером 500MB. Загрузить в память весь файл — это неэффективно. Генератор позволил обрабатывать по 1000 строк за раз:

const fs = require('fs');
const readline = require('readline');

function* readLargeFile(filePath) {
  const fileStream = fs.createReadStream(filePath);
  const rl = readline.createInterface({
    input: fileStream,
    crlfDelay: Infinity
  });

  for await (const line of rl) {
    yield line;
  }
}

// Использование
const csvGenerator = readLargeFile('users.csv');
let processedCount = 0;

for (const line of csvGenerator) {
  const user = parseCSV(line);
  saveToDatabase(user);
  processedCount++;

  // Логируем прогресс
  if (processedCount % 1000 === 0) {
    console.log(`Обработано ${processedCount} пользователей`);
  }
}

2. Асинхронная обработка с задержками (rate limiting)

Нужно было обработать API запросы с ограничением 10 запросов в секунду:

function* rateLimiter(items, requestsPerSecond) {
  const delayMs = 1000 / requestsPerSecond;
  let lastTime = Date.now();

  for (const item of items) {
    const now = Date.now();
    const timeSinceLastRequest = now - lastTime;

    if (timeSinceLastRequest < delayMs) {
      yield new Promise(resolve =>
        setTimeout(resolve, delayMs - timeSinceLastRequest)
      );
    }

    yield fetch(`/api/users/${item.id}`);
    lastTime = Date.now();
  }
}

// Использование
(async () => {
  const items = [{ id: 1 }, { id: 2 }, { id: 3 }];
  for await (const request of rateLimiter(items, 10)) {
    const response = await request;
    console.log(response.status);
  }
})();

3. Обход сложных структур данных (tree traversal)

Нужно было обойти древовидную структуру папок и файлов:

function* walkDir(dir) {
  const files = fs.readdirSync(dir, { withFileTypes: true });

  for (const file of files) {
    const fullPath = path.join(dir, file.name);

    if (file.isDirectory()) {
      // Рекурсивный обход
      yield* walkDir(fullPath);
    } else {
      yield fullPath;
    }
  }
}

// Использование
for (const filePath of walkDir('/path/to/dir')) {
  console.log(`Найден файл: ${filePath}`);
  if (filePath.endsWith('.log')) {
    deleteFile(filePath);
  }
}

4. Paginated API запросы (infinite scroll)

Работал с API, который возвращает данные paginated. Нужно было автоматически запрашивать следующую страницу:

function* fetchPaginatedData(apiUrl) {
  let page = 1;
  let hasMore = true;

  while (hasMore) {
    const response = await fetch(`${apiUrl}?page=${page}`);
    const data = await response.json();

    if (data.items.length === 0) {
      hasMore = false;
      return;
    }

    for (const item of data.items) {
      yield item;
    }

    page++;
  }
}

// Использование
for await (const item of fetchPaginatedData('/api/products')) {
  console.log(item.name);
  if (itemCount++ === 100) break; // Читаем первые 100 товаров
}

5. Создание простого event emitter'а

Использовал генератор для имитации event stream'а:

function* eventStream(userId) {
  // Слушаем события в БД
  while (true) {
    const event = await db.query(
      'SELECT * FROM events WHERE user_id = @id AND processed = false LIMIT 1',
      { id: userId }
    );

    if (event) {
      yield event;
      await db.query('UPDATE events SET processed = true WHERE id = @id', { id: event.id });
    } else {
      // Ждём 100ms перед следующей проверкой
      await new Promise(resolve => setTimeout(resolve, 100));
    }
  }
}

// Использование
const stream = eventStream(userId);
for await (const event of stream) {
  handleEvent(event);
}

Генераторы vs Асинхронные функции

До ES2017 (генераторы):

// Асинхронный код через генератор + промисы
function* fetchUserData(userId) {
  const user = yield fetch(`/api/users/${userId}`).then(r => r.json());
  const posts = yield fetch(`/api/users/${userId}/posts`).then(r => r.json());
  return { user, posts };
}

// Нужен "коррунтин" для управления
function runGenerator(gen) {
  const iterator = gen();
  function handle(result) {
    if (result.done) return Promise.resolve(result.value);
    return Promise.resolve(result.value)
      .then(res => handle(iterator.next(res)))
      .catch(err => iterator.throw(err));
  }
  return handle(iterator.next());
}

runGenerator(() => fetchUserData(1));

После ES2017 (async/await):

// Это буквально сахар над генераторами, но удобнее
async function fetchUserData(userId) {
  const user = await fetch(`/api/users/${userId}`).then(r => r.json());
  const posts = await fetch(`/api/users/${userId}/posts`).then(r => r.json());
  return { user, posts };
}

await fetchUserData(1);

Вывод: Для асинхронного кода теперь используем async/await, но генераторы остаются полезны для:

  • Итерирования больших данных
  • Lazy evaluation
  • Реализации паттернов (как выше)

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

// ✗ Неправильно: сложнее, чем обычный цикл
function* simpleLoop() {
  for (let i = 0; i < 10; i++) {
    yield i;
  }
}

// ✓ Правильно: просто
for (let i = 0; i < 10; i++) {
  console.log(i);
}

Практический вывод

Генераторы — это инструмент для lazy evaluation и больших данных. Они позволяют:

  • Обрабатывать данные по частям (не загружая всё в память)
  • Приостанавливать выполнение и возобновлять его
  • Реализовывать custom iteration протоколы

Для асинхронного кода лучше использовать async/await.

Как использовал генератор? | PrepBro