Как использовал генераторы в JavaScript/Node.js?

Ответ

Генераторы в JavaScript — это особые функции (function*), которые могут приостанавливать своё выполнение с помощью ключевого слова yield и возобновлять его позже. В Node.js я применял их для решения специфических задач, где нужен контроль над потоком выполнения.

Базовый пример:

function* generateSequence() {
  yield 1;
  yield 2;
  return 3;
}

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

Мои практические кейсы использования в Node.js:

  1. Ленивая генерация данных или ID:

    function* idGenerator() {
      let id = 0;
      while (true) {
        yield ++id;
      }
    }
    const getId = idGenerator();
    console.log(getId.next().value); // 1
    console.log(getId.next().value); // 2
    // Полезно, когда нужно генерировать ID по требованию, не создавая массив заранее.
  2. Работа с большими данными или потоками: Генераторы отлично сочетаются с новыми возможностями Node.js, например, с асинхронными итераторами для чтения больших файлов построчно.

  3. Реализация кастомных итераторов для сложных структур данных.

  4. Кооперативная многозадачность (редко): С помощью библиотек вроде co (популярной до появления async/await) генераторы использовались для написания асинхронного кода в синхронном стиле.

Важно: С появлением async/await необходимость в генераторах для управления асинхронностью отпала. Сейчас их основная ниша — создание лениво вычисляемых последовательностей и работа как итераторов.

Ответ 18+ 🔞

Слушай, я тут подумал про генераторы в JavaScript, и знаешь, это ж просто ёперный театр какой-то. Выглядит как обычная функция, но с одной звёздочкой — function*. И эта тварь умеет ставить себя на паузу словно по команде yield, а потом, когда захочешь, продолжить с того же места. В Node.js я эту штуку применял, когда нужно было управлять потоком выполнения как царь и бог, а не как раб событийного цикла.

Вот смотри, элементарщина:

function* generateSequence() {
  yield 1;
  yield 2;
  return 3;
}

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

Видишь? Ты её дёргаешь методом .next(), а она тебе плюётся очередным значением и засыпает. Красота, блядь.

А вот где я это реально втыкал в Node.js:

  1. Ленивая генерация чего угодно, например, ID. Чтоб не городить массив из миллиона чисел заранее.

    function* idGenerator() {
      let id = 0;
      while (true) {
        yield ++id;
      }
    }
    const getId = idGenerator();
    console.log(getId.next().value); // 1
    console.log(getId.next().value); // 2
    // Бери ID по одному, когда нужно — удобно, ёпта.
  2. Работа с огромными данными или потоками. Особенно когда Node.js подрос и появились асинхронные итераторы. Генератор может читать здоровенный файл построчно и отдавать тебе строки по мере чтения, не загружая всё в память разом. Это уже не хитрая жопа, а гениальная просто.

  3. Кастомные итераторы для своих хитровыебанных структур данных. Когда стандартного for...of мало и хочется контролировать каждый шаг.

  4. Кооперативная многозадачность (но это уже древность). Раньше, до того как async/await всех спас, были библиотеки вроде co. Они использовали генераторы, чтобы писать асинхронный код, который выглядел как синхронный. Сейчас на это смотришь и думаешь — ни хуя себе, как мы раньше жили-то.

Но вот главное, запомни: Сейчас, когда у нас есть async/await, использовать генераторы для асинхронности — это как ехать на Москвиче 412, когда в гараже стоит нормальная тачка. Их настоящая сила сейчас — это создание этих самых ленивых последовательностей и работа в качестве продвинутых итераторов. Всё остальное — уже история, чувак.