Sobes.tech
Middle+

Proč běžné generátory umožňují psát asynchronní kód?

sobes.tech AI

Odpověď od AI

Obvyklé generátory v JavaScriptu umožňují psát asynchronní kód díky jejich schopnosti pozastavit a znovu spustit provádění, stejně jako předávat hodnoty oběma směry (yield a next()).

Zde je, jak to funguje v kontextu asynchronnosti:

  1. Pozastavení provádění (yield): Když dosáhne příkazu yield, generátor se pozastaví a vrátí hodnotu. V asynchronním kódu může tato hodnota představovat promise nebo jinou "akci", kterou je třeba asynchronně provést.

  2. Čekání na asynchronní výsledek: Vnější kód (například "runner" nebo obal pro generátor) obdrží hodnotu předanou přes yield. Pokud je to promise, čeká na její vyřešení nebo odmítnutí.

  3. Obnovení provádění (next()): Po dokončení asynchronní operace (vyřešení promise) je asynchronní výsledek (hodnota promise) vrácena zpět do generátoru přes metodu next(). Generátor pokračuje v provádění od místa, kde byl pozastaven.

  4. Předání výsledku: Hodnota předaná do next() se stává výsledkem výrazu yield uvnitř generátoru. Tím může generátor "získat" výsledek asynchronní operace a pokračovat s ním, jako by kód byl synchronní.

Tento vzor vytváří iluzi synchronního průběhu provádění pro asynchronní operace, což činí kód čitelnějším a lépe spravovatelným ve srovnání s tradičním použitím callbacků nebo složitých řetězců .then().

Příkladem takového obalu je typ co (ačkoliv nyní jsou běžnější async/await, které jsou v podstatě syntaktickým cukrem nad generátory a promise):

// Příklad použití generátoru pro asynchronitu
function* myAsyncGenerator() {
  try {
    const result1 = yield Promise.resolve(1); // Pozastavení, vrácení promise
    console.log('Získali jsme první výsledek:', result1); // Obnovení s předáním výsledku

    const result2 = yield new Promise(resolve => setTimeout(() => resolve(2), 500)); // Další asynchronní operace
    console.log('Získali jsme druhý výsledek:', result2);

    return 'Hotovo'; // Ukončení generátoru
  } catch (error) {
    console.error('Došlo k chybě:', error);
  }
}

// Jednoduchý "runner" pro generátor (zjednodušeno)
function run(generatorFunc) {
  const generator = generatorFunc();

  function handleNext(newValue) {
    const result = generator.next(newValue);

    if (!result.done) {
      if (result.value instanceof Promise) {
        result.value.then(handleNext).catch(handleError);
      } else {
        handleNext(result.value);
      }
    } else {
      console.log('Generátor byl ukončen s výsledkem:', result.value);
    }
  }

  function handleError(error) {
      generator.throw(error);
  }

  handleNext(); // Spuštění generátoru
}

// Spouštíme náš asynchronní generátor
run(myAsyncGenerator);

V tomto příkladu yield Promise.resolve(1) pozastaví generátor. "Runner" (nebo co) čeká, až se promise vyřeší, a poté znovu spustí generátor, předávající hodnotu 1 přes next(1). Tato hodnota 1 se stává výsledkem yield Promise.resolve(1) uvnitř generátoru, což umožňuje zpracovat ji na následujícím řádku (console.log). Tento mechanismus umožňuje napodobit synchronní tok provádění u asynchronních operací.