Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Функции-генераторы в 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, который понятнее и проще в поддержке.