Domů O mně Služby Portfolio Učím Blog Kontakt
← Zpět na učím

7.11 Generátory

Výuka

Co jsou generátory a proč existují?

Běžná funkce v JavaScriptu běží od začátku do konce najednou - vrátí hodnotu a skončí. Generátor je speciální typ funkce, která může pozastavit své vykonávání a později pokračovat tam, kde skončila.

Představ si to jako knihu: Běžná funkce je jako přečíst celou knihu najednou. Generátor je jako záložka - můžeš přestat číst, zavřít knihu, a později pokračovat přesně tam, kde jsi skončil.

K čemu jsou generátory užitečné?

  1. Líné vyhodnocování (lazy evaluation) - generuješ hodnoty až když je potřebuješ
  2. Nekonečné sekvence - můžeš vytvořit "nekonečný" seznam bez zahlcení paměti
  3. Řízení toku - pozastavení a pokračování výpočtu
  4. Iterátory na míru - vytvoření vlastních iterovatelných objektů

Syntaxe generátoru

function* nazevGeneratoru() {
  yield hodnota1;
  yield hodnota2;
  return finalniHodnota;
}

Klíčové prvky:

  • function* - hvězdička označuje generátor (může být i function * nebo function*)
  • yield - "vydá" hodnotu a pozastaví funkci
  • return - ukončí generátor s finální hodnotou

Jak generátor funguje?

1. Zavoláš generátor  vrátí iterátor (objekt), NE výsledek
2. Zavoláš .next()  spustí se  k prvnímu yield
3. Vrátí { value: hodnota, done: false }
4. Zavoláš .next() znovu  pokračuje od minulého yield k dalšímu
5. Když dojde na return  { value: finalniHodnota, done: true }

JavaScript

// Základní generátor
function* simpleGenerator() {
  console.log("Začátek");
  yield 1;
  console.log("Po prvním yield");
  yield 2;
  console.log("Po druhém yield");
  return 3;
}

const gen = simpleGenerator();  // Nevypíše nic! Jen vytvoří iterátor

console.log(gen.next());  // "Začátek" → { value: 1, done: false }
console.log(gen.next());  // "Po prvním yield" → { value: 2, done: false }
console.log(gen.next());  // "Po druhém yield" → { value: 3, done: true }
console.log(gen.next());  // { value: undefined, done: true }

// Iterace s for...of
function* colors() {
  yield "červená";
  yield "zelená";
  yield "modrá";
}

for (const color of colors()) {
  console.log(color);
}
// "červená", "zelená", "modrá"

// Spread operátor
const allColors = [...colors()];
// ["červená", "zelená", "modrá"]

// Nekonečný generátor
function* infiniteNumbers() {
  let n = 0;
  while (true) {
    yield n++;
  }
}

const nums = infiniteNumbers();
console.log(nums.next().value);  // 0
console.log(nums.next().value);  // 1
console.log(nums.next().value);  // 2
// Pokračuje do nekonečna...

// Praktický příklad: range
function* range(start, end, step = 1) {
  for (let i = start; i < end; i += step) {
    yield i;
  }
}

for (const n of range(0, 10, 2)) {
  console.log(n);  // 0, 2, 4, 6, 8
}

// Předávání hodnot DO generátoru
function* conversation() {
  const name = yield "Jak se jmenuješ?";
  const hobby = yield `Ahoj ${name}! Co tě baví?`;
  return `${name} má rád/a ${hobby}`;
}

const chat = conversation();
console.log(chat.next().value);        // "Jak se jmenuješ?"
console.log(chat.next("Jana").value);  // "Ahoj Jana! Co tě baví?"
console.log(chat.next("programování").value);  // "Jana má rád/a programování"

// yield* - delegování na jiný iterátor
function* inner() {
  yield 2;
  yield 3;
}

function* outer() {
  yield 1;
  yield* inner();  // Deleguje na inner generátor
  yield 4;
}

console.log([...outer()]);  // [1, 2, 3, 4]

// Generátor jako ID generátor
function* idGenerator(prefix = "id") {
  let id = 1;
  while (true) {
    yield `${prefix}_${id++}`;
  }
}

const userIds = idGenerator("user");
console.log(userIds.next().value);  // "user_1"
console.log(userIds.next().value);  // "user_2"

// Fibonacci pomocí generátoru
function* fibonacci() {
  let [prev, curr] = [0, 1];
  while (true) {
    yield curr;
    [prev, curr] = [curr, prev + curr];
  }
}

const fib = fibonacci();
for (let i = 0; i < 10; i++) {
  console.log(fib.next().value);
}
// 1, 1, 2, 3, 5, 8, 13, 21, 34, 55

TypeScript

// Typování generátoru: Generator<Yield, Return, Next>
// - Yield: typ hodnot které yield vrací
// - Return: typ hodnoty kterou return vrací
// - Next: typ hodnot přijímaných přes .next()

function* numberGenerator(): Generator<number, void, unknown> {
  yield 1;
  yield 2;
  yield 3;
}

// Jednoduší zápis - TS často odvodí typy
function* colors(): Generator<string> {
  yield "červená";
  yield "zelená";
  yield "modrá";
}

// Range s typy
function* range(start: number, end: number, step: number = 1): Generator<number> {
  for (let i = start; i < end; i += step) {
    yield i;
  }
}

// Generátor s návratovou hodnotou
function* countdown(from: number): Generator<number, string, unknown> {
  while (from > 0) {
    yield from--;
  }
  return "Start!";
}

const counter = countdown(3);
console.log(counter.next());  // { value: 3, done: false }
console.log(counter.next());  // { value: 2, done: false }
console.log(counter.next());  // { value: 1, done: false }
console.log(counter.next());  // { value: "Start!", done: true }

// Generátor přijímající hodnoty
function* accumulator(): Generator<number, number, number> {
  let total = 0;
  while (true) {
    const input: number = yield total;
    total += input;
  }
}

const acc = accumulator();
acc.next();           // Inicializace, { value: 0, done: false }
acc.next(5);          // { value: 5, done: false }
acc.next(10);         // { value: 15, done: false }
acc.next(3);          // { value: 18, done: false }

// Async generátor
async function* fetchPages(urls: string[]): AsyncGenerator<Response> {
  for (const url of urls) {
    yield await fetch(url);
  }
}

// Použití async generátoru
async function processPages() {
  const urls = ["/api/page1", "/api/page2", "/api/page3"];

  for await (const response of fetchPages(urls)) {
    const data = await response.json();
    console.log(data);
  }
}

// Iterátor pomocí generátoru v třídě
class NumberRange implements Iterable<number> {
  constructor(
    private start: number,
    private end: number
  ) {}

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

const range5to10 = new NumberRange(5, 10);
console.log([...range5to10]);  // [5, 6, 7, 8, 9, 10]

// Typově bezpečný ID generátor
function* createIdGenerator<T extends string>(
  prefix: T
): Generator<`${T}_${number}`, never, unknown> {
  let id = 1;
  while (true) {
    yield `${prefix}_${id++}` as `${T}_${number}`;
  }
}

const userIdGen = createIdGenerator("user");
const id1 = userIdGen.next().value;  // "user_1" (typ: `user_${number}`)

Rozdíl JS vs TS

JavaScript TypeScript
Žádné typování generátoru Generator
yield vrací any Přesný typ yield hodnot
.next() přijímá cokoliv Typová kontrola vstupů
Async generátor bez typů AsyncGenerator
// TypeScript rozlišuje 3 typy generátoru:
// Generator<Yield, Return, Next>

function* example(): Generator<string, number, boolean> {
  //                            ^^^^^^  ^^^^^^  ^^^^^^^
  //                            yield   return  .next() input

  const input: boolean = yield "hello";  // yield vrací string
  if (input) {
    return 42;  // return vrací number
  }
  return 0;
}

const gen = example();
gen.next();        // { value: "hello", done: false }
gen.next(true);    // { value: 42, done: true }

Tip

Kdy použít generátory:

// 1. Líné zpracování velkých dat
function* processLargeFile(lines) {
  for (const line of lines) {
    yield line.trim().toUpperCase();
  }
}
// Nezpracuje VŠECHNY řádky najednou - jen když potřebuješ

// 2. Stavové automaty
function* trafficLight() {
  while (true) {
    yield "červená";
    yield "oranžová";
    yield "zelená";
    yield "oranžová";
  }
}

// 3. Unikátní ID bez globální proměnné
const getId = (function*() {
  let id = 0;
  while (true) yield id++;
})();

getId.next().value;  // 0
getId.next().value;  // 1

yield* pro kompozici:

function* ones(n) {
  for (let i = 0; i < n; i++) yield 1;
}

function* zeros(n) {
  for (let i = 0; i < n; i++) yield 0;
}

function* pattern() {
  yield* ones(3);   // 1, 1, 1
  yield* zeros(2);  // 0, 0
  yield* ones(2);   // 1, 1
}

console.log([...pattern()]);  // [1, 1, 1, 0, 0, 1, 1]

Async generátory pro streaming:

async function* streamData(url) {
  const response = await fetch(url);
  const reader = response.body.getReader();

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    yield value;
  }
}

Kvíz

Které výroky o generátorech jsou pravdivé?

- Generátor při zavolání vrátí iterátor, ale nespustí se. Spustí se až při prvním .next()

- yield pozastaví funkci, vrátí hodnotu a čeká na další .next()

- Generátor je iterovatelný, takže funguje s for...of, spread operátorem atd.

- Po return je generátor ukončen (done: true), další .next() vrací undefined

🎯 Závěrečný projekt

Po dokončení všech 8 dílů vytvoříte jednoduchou Todо aplikaci v čistém JavaScriptu. Naučíte se, jak aplikovat vše, co jste se naučili, na reálný projekt.

Zobrazit podrobnosti projektu →

Připraveni začít?

Zaregistrujte se a získejte přístup ke všem dílům tohoto seriálu

Kontaktujte mě

Informace o seriálu

Obtížnost

Délka

Cca 480 minut

Počet videí

8 videí + projekty

Certifikát

Po dokončení obdržíte certifikát


Lekce v této sekci


Struktura lekcí (souborový strom)

06. Typescript specifika
  • v přípravě
08. Moduly tridy
  • v přípravě
09. React zaklady
  • v přípravě
10. React hooks
  • v přípravě
12. Nextjs server
  • v přípravě
13. Databaze auth
  • v přípravě
14. Nextjs pokrocile
  • v přípravě