Sobes.tech
Middle+

De ce generatorii obișnuiți permit scrierea de cod asincron?

sobes.tech AI

Răspuns de la AI

Generatorii obișnuiți în JavaScript permit scrierea de cod asincron datorită capacității lor de a suspenda și relua execuția, precum și de a transmite valori în ambele direcții (prin yield și next()).

Iată cum funcționează în contextul asincronicității:

  1. Suspendarea execuției (yield): Când se ajunge la instrucțiunea yield, generatorul se suspendă și returnează o valoare. În codul asincron, această valoare poate fi o promisiune (promise) sau o altă "acțiune" care trebuie executată asincron.

  2. Așteptarea rezultatului asincron: Codul extern (de exemplu, un "runner" sau un wrapper pentru generator) primește valoarea transmisă prin yield. Dacă este o promisiune, așteaptă rezolvarea sau respingerea acesteia.

  3. Reluarea execuției (next()): După finalizarea operației asincrone (rezolvarea promisiunii), rezultatul asincron (valoarea promisiunii) este transmis înapoi generatorului prin metoda next(). Generatorul își continuă execuția de unde a fost suspendat.

  4. Transmiterea rezultatului: Valoarea transmisă în next() devine rezultatul expresiei yield din interiorul generatorului. Astfel, generatorul poate "obține" rezultatul operației asincrone și poate continua să lucreze cu el, ca și cum codul ar fi sincron.

Acest pattern creează o iluzie de execuție sincronă pentru operațiile asincrone, făcând codul mai ușor de citit și de gestionat comparativ cu utilizarea tradițională a callback-urilor sau a lanțurilor complexe .then().

Un exemplu de acest tip sunt wrapper-urile precum co (deși acum sunt mai frecvent async/await, care sunt în esență zahăr sintactic peste generatori și promisiuni):

// Exemplu de utilizare a unui generator pentru asincronie
function* myAsyncGenerator() {
  try {
    const result1 = yield Promise.resolve(1); // Suspendare, returnează promisiune
    console.log('Primul rezultat:', result1); // Reîncepe cu valoarea

    const result2 = yield new Promise(resolve => setTimeout(() => resolve(2), 500)); // Altă operație asincronă
    console.log('Al doilea rezultat:', result2);

    return 'Gata'; // Încheierea generatorului
  } catch (error) {
    console.error('A apărut o eroare:', error);
  }
}

// "Runner" simplu pentru generator (simplificat)
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('Generatorul s-a încheiat cu rezultatul:', result.value);
    }
  }

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

  handleNext(); // Începem generatorul
}

// Rulăm generatorul nostru asincron
run(myAsyncGenerator);

În acest exemplu, yield Promise.resolve(1) suspendă generatorul. "Runner-ul" (sau co) așteaptă ca promisiunea să se rezolve și apoi reia generatorul, transmitând valoarea 1 prin next(1). Această valoare 1 devine rezultatul lui yield Promise.resolve(1) din interiorul generatorului, permițându-i să fie procesată pe următoarea linie (console.log). Acest mecanism permite imitarea unui flux de execuție sincron pentru operații asincrone.