Das Advanced JavaScript Guide: Generatoren. Teil 2, ein einfacher Anwendungsfall



Das Verhalten der im vorherigen Artikel beschriebenen Generatoren ist nicht komplex, aber es ist definitiv überraschend und mag zunächst verwirrend erscheinen. Anstatt neue Konzepte zu lernen, werden wir jetzt ein interessantes Beispiel für die Verwendung von Generatoren betrachten.



Lassen Sie uns eine Funktion wie diese haben:



function maybeAddNumbers() {
    const a = maybeGetNumberA();
    const b = maybeGetNumberB();

    return a + b;
}

      
      





Funktionen maybeGetNumberA



und maybeGetNumberB



Rückgabewerte, aber manchmal können sie null



oder zurückgeben undefined



. Dies wird durch das Wort "vielleicht" in ihren Namen belegt. Versuchen Sie in diesem Fall nicht, diese Werte (z. B. die Zahl und null



) einzugeben null



. Es ist beispielsweise besser, anzuhalten und zurückzukehren . Und zwar null



nicht irgendein unvorhersehbarer Wert, der durch Hinzufügen von null



/ undefined



mit einer Zahl oder einem anderen null



/ erhalten wird undefined



.



Sie müssen also überprüfen, ob die Zahlen tatsächlich definiert sind:



function maybeAddNumbers() {
    const a = maybeGetNumberA();
    const b = maybeGetNumberB();

    if (a === null || a === undefined || b === null || b === undefined) {
        return null;
    }

    return a + b;
}

      
      





Alles funktioniert, aber wenn es a



ist null



oder undefined



, dann gibt es keinen Punkt die Funktion im Aufruf maybeGetNumberB



. Wir wissen, dass es trotzdem zurückgegeben wird null



.



Schreiben wir die Funktion neu:



function maybeAddNumbers() {
    const a = maybeGetNumberA();

    if (a === null || a === undefined) {
        return null;
    }

    const b = maybeGetNumberB();

    if (b === null || b === undefined) {
        return null;
    }

    return a + b;
}

      
      





Damit. Anstelle von drei einfachen Codezeilen haben wir sie schnell auf 10 Zeilen aufgebläht (ohne leere). Und jetzt werden Funktionen angewendet if



, die Sie durchlaufen müssen, um zu verstehen, was die Funktion tut. Und dies ist nur ein lehrreiches Beispiel! Stellen Sie sich eine echte Codebasis mit viel komplexerer Logik vor, die solche Überprüfungen noch schwieriger macht. Ich möchte hier Generatoren verwenden und den Code vereinfachen.



Schau mal:



function* maybeAddNumbers() {
    const a = yield maybeGetNumberA();
    const b = yield maybeGetNumberB();

    return a + b;
}

      
      





Was wäre, wenn wir den Ausdruck yield <smething>



testen lassen könnten, ob es sich um einen <smething>



echten Wert handelt, und nicht null



oder undefined



? Wenn sich herausstellt, dass es sich nicht um eine Zahl handelt, halten wir einfach an und kehren zurück null



, wie in der vorherigen Version des Codes.



Das heißt, Sie können Code schreiben, der so aussieht, als würde er nur mit realen, definierten Werten funktionieren. Der Generator kann dies überprüfen und entsprechende Maßnahmen für Sie ergreifen! Magie, richtig? Und es ist nicht nur möglich, es ist einfach zu schreiben!



Natürlich haben die Generatoren selbst diese Funktionalität nicht. Sie geben nur Iteratoren zurück, und Sie können Werte wieder in die Generatoren einfügen, wenn Sie möchten. Also müssen wir einen Wrapper schreiben, also sei es runMaybe



.



Anstatt die Funktion direkt aufzurufen:



const result = maybeAddNumbers();

      
      





Wir werden es als Wrapper-Argument bezeichnen:



const result = runMaybe(maybeAddNumbers());

      
      





Dieses Muster ist bei Generatoren sehr häufig. An sich wissen sie nicht viel, aber mit Hilfe von selbstgeschriebenen Wrappern können Sie den Generatoren das gewünschte Verhalten geben! Das brauchen wir jetzt.



runMaybe



- eine Funktion, die ein Argument akzeptiert: einen vom Generator erstellten Iterator:



function runMaybe(iterator) {

}

      
      





Lassen Sie uns diesen Iterator in einer Schleife ausführen while



. Dazu müssen Sie den Iterator zum ersten Mal aufrufen und seine Eigenschaft überprüfen done



:



function runMaybe(iterator) {
    let result = iterator.next();

    while(!result.done) {

    }
}

      
      





Innerhalb der Schleife haben wir zwei Möglichkeiten. Wenn result.value



is null



oder undefined



, sollte die Iteration sofort gestoppt und zurückgegeben werden null



. Lass uns das machen:



function runMaybe(iterator) {
    let result = iterator.next();

    while(!result.done) {
        if (result.value === null || result.value === undefined) {
            return null;
        }
    }
}

      
      





Hier return



stoppen wir sofort die Iteration mit Hilfe und kehren vom Wrapper zurück null



. Wenn es sich jedoch result.value



um eine Zahl handelt, müssen Sie zum Generator "zurückkehren". Wenn die yield maybeGetNumberA()



Funktion maybeGetNumberA()



beispielsweise eine Zahl ist, müssen Sie den yield maybeGetNumberA()



Wert dieser Zahl ersetzen . Lassen Sie mich erklären: Lassen Sie uns sagen , dass das Ergebnis der Berechnung maybeGetNumberA()



ist 5, dann ersetzen wir const a = yield maybeGetNumberA();



mit const a = 5;



. Wie Sie sehen können, wir brauchen nicht zu dem extrahierten Wert zu ändern, ist es genug , um es zu passieren zurück zum Generator.



Wir erinnern uns, dass Sie durch yield <smething>



einen Wert ersetzen können, indem Sie ihn als Argument an die Methode übergeben next



in einem Iterator:



function runMaybe(iterator) {
    let result = iterator.next();

    while(!result.done) {
        if (result.value === null || result.value === undefined) {
            return null;
        }

        // we are passing result.value back
        // to the generator
        result = iterator.next(result.value)
    }
}

      
      





Wie Sie sehen, wird das neue Ergebnis nun wieder in einer Variablen gespeichert result



. Dies ist möglich, weil wir ausdrücklich die result



Verwendung von deklariert haben let



.



Wenn der Generator beim Abrufen eines Werts auf ein null



/ stößt undefined



, kehren wir einfach null



vom Wrapper zurück runMaybe



.



Es bleibt noch etwas hinzuzufügen, damit der Iterationsprozess endet, ohne null



/ zu erkennen undefined



. Wenn wir zwei Zahlen erhalten, müssen wir schließlich ihre Summe aus dem Wrapper zurückgeben!



Der Generator maybeAddNumbers



endet mit einem Ausdruck return



. Wir verstehen, dass die Anwesenheit return <smething>



im Generator bewirkt, dass next



ein Objekt vom Aufruf zurückgegeben wird { value: <smething>, done: true }



. In diesem Fall while



stoppt die Schleife, da die Eigenschaft done



einen Wert erhält true



. Der zuletzt zurückgegebene Wert (in unserem speziellen Fall dieser a + b



) wird jedoch weiterhin in der Eigenschaft gespeichert result.value



! Und wir können es einfach zurückgeben:



function runMaybe(iterator) {
    let result = iterator.next();

    while(!result.done) {
        if (result.value === null || result.value === undefined) {
            return null;
        }

        result = iterator.next(result.value)
    }

    // just return the last value
    // after the iterator is done
    return result.value;
}

      
      





Und das ist alles!



Lassen Sie uns Funktionen maybeGetNumberA



und erstellen und maybeGetNumberB



sie zuerst reelle Zahlen zurückgeben:



const maybeGetNumberA = () => 5;
const maybeGetNumberB = () => 10;

      
      





Lassen Sie uns den Code ausführen und das Ergebnis protokollieren:



function* maybeAddNumbers() {
    const a = yield maybeGetNumberA();
    const b = yield maybeGetNumberB();

    return a + b;
}

const result = runMaybe(maybeAddNumbers());

console.log(result);

      
      





Wie erwartet wird die Nummer 15 in der Konsole



angezeigt. Ersetzen Sie nun einen der Begriffe durch null



:



const maybeGetNumberA = () => null;
const maybeGetNumberB = () => 10;

      
      





Bei der Ausführung des Codes erhalten wir null



!



Es ist jedoch wichtig, dass wir sicherstellen, dass die Funktion maybeGetNumberB



nicht aufgerufen wird, wenn sie / maybeGetNumberA



zurückgibt . Lassen Sie uns noch einmal überprüfen, ob die Berechnung erfolgreich war. Fügen Sie dazu einfach die zweite Funktion hinzu : null



undefined



console.log







const maybeGetNumberA = () => null;
const maybeGetNumberB = () => {
    console.log('B');
    return 10;
}

      
      





Wenn wir den Wrapper korrekt geschrieben haben runMaybe



, wird der Buchstabe bei der Ausführung dieses Codes B



nicht in der Konsole angezeigt.



In der Tat, wenn wir den Code ausführen, werden wir einfach sehen null



. Dies bedeutet, dass der Wrapper den Generator tatsächlich stoppt, sobald er null



/ erkennt undefined



.



Der Code funktioniert wie vorgesehen: Er erzeugt eine null



beliebige Kombination:



const maybeGetNumberA = () => undefined;
const maybeGetNumberB = () => 10;
const maybeGetNumberA = () => 5;
const maybeGetNumberB = () => null;
const maybeGetNumberA = () => undefined;
const maybeGetNumberB = () => null;

      
      





Usw.



Der Vorteil dieses Beispiels liegt jedoch nicht in der Ausführung dieses bestimmten Codes. Es liegt in der Tatsache , dass wir eine erstellt haben universal Wrapper, der arbeiten kann jeden Generator, den Wert extrahieren null



/ undefined



.



Schreiben wir eine komplexere Funktion:



function* maybeAddFiveNumbers() {
    const a = yield maybeGetNumberA();
    const b = yield maybeGetNumberB();
    const c = yield maybeGetNumberC();
    const d = yield maybeGetNumberD();
    const e = yield maybeGetNumberE();
    
    return a + b + c + d + e;
}

      
      





Sie können dies problemlos in unserem Wrapper tun runMaybe



! Tatsächlich ist es für den Wrapper nicht einmal wichtig, dass unsere Funktionen Zahlen zurückgeben. Immerhin haben wir den numerischen Typ darin nicht erwähnt. Sie können also jeden Wert im Generator verwenden - Zahlen, Zeichenfolgen, Objekte, Arrays, komplexere Datenstrukturen - und es funktioniert mit unserem Wrapper!



Das inspiriert Entwickler. Mit Generatoren können Sie Ihrem Code benutzerdefinierte Funktionen hinzufügen, die sehr häufig vorkommen (abgesehen von Aufrufen natürlich yield



). Sie müssen nur einen Wrapper erstellen, der den Generator auf besondere Weise iteriert. Somit fügt der Wrapper dem Generator die notwendige Funktionalität hinzu, die alles sein kann! Generatoren haben nahezu unbegrenzte Möglichkeiten, es geht nur um unsere Vorstellungskraft.



All Articles