Spaghetti Code

Promises sind in Javascript kaum wegzudenken und ein fundamentales Werkzeug um asynchrone Aufrufe zu bewerkstelligen. Oft werden Promises jedoch schlecht genutzt und erzeugen schlecht lesbaren Code. In diesem Blogeintrag möchte ich zeigen wie man zueinander abhängige Promises besser kombinieren kann.

Promises wurden in den Standard für Javascript mit ES6 übernommen. Davor gab es diverse Libraries wie etwa JQuery oder AngularJS die eigene Implementierungen davon hatten. Promises sind Versprechen die später eingelöst werden (eine schöne Metapher). Davor wurden oft Callback Funktionen übergeben die im Verlauf des Programmes dann aufgerufen werden. Mit Promises kann man definieren dass, wenn ein bestimmter Codeteil, auf den man wartet, fertig ist, die definierte Logik danach ausgeführt wird. Im Fehlerfall kann und sollte eine andere Logik ausgeführt werden. Das tolle daran ist, das man auch Abhängigkeiten zwischen solchen Promises definieren kann – nur leider werden diese oft umständlicher programmiert als notwendig.

Wie einige KollegInnen wissen, bin ich leidenschaftlicher Spaghetti Bolognese Koch und Esser. Daher liegt es nur Nahe, einmal ein Konzept anhand der italienischen Küche zu erklären. Schreiben wir also bisschen Spaghetti Code – und das im positiven Sinne.

Bis ich mein Essen am Tisch habe muss ich jedoch einiges tun. Die Spaghetti Soße und die Nudeln müssen gekocht werden. Ich habe in unserem Beispiel also einmal eine Funktion „spaghettiKochen“ und „bologneseKochen“.

Für die Demonstration kann der Code in etwa so aussehen:

Unsere Spaghetti dürfen zwischen 10 und 12 Minuten gekocht werden ansonsten sind sie verkocht oder noch roh. Die „bologneseKochen“ Methode ist hier abstrakt gehalten und wird einfach nach 100ms fertig. Damit wir generell nicht so lange warten müssen sind alle Minuten hier in Millisekunden abgebildet. Für 10 Minuten beim wirklichen kochen muss ich in unserem Code 100ms warten.

Der erste Versuch dies zu programmieren könnte in etwa so aussehen:

Ich koche zehn Minuten die Spaghetti und beginne dann mit der Bolognese. Hätte ich weniger als 10 Minuten die Spaghetti gekocht oder länger als 12 Minuten, dann wäre mein italienischer Traum geplatzt und ich hätte mir stattdessen eine Pizza vom Lieferservice bestellt.

Wie man sieht, führe ich „spaghettiKochen“ zuerst aus und danach „bologneseKochen“. Als erfahrener und effizienter Koch kann ich das jedoch mit zwei Töpfen am Herd gleichzeitig tun. Ich muss keine 10 Minuten warten und dann erst mit der Bolognese beginnen. Mein Essen ist jedoch trotzdem erst fertig, wenn beides abgeschlossen ist. Da beide nicht wirklich etwas gemeinsam haben kann ich beide gleichzeitig starten. Es gibt also eine bessere Variante:

In diesem Beispiel werden „spaghettiKochen“ und „bologneseKochen“ gleichzeitig ausgeführt und wir warten auf das Ergebnis beider Funktionen. Wenn etwas davon nicht klappt, dann bestelle ich wieder etwas beim Lieferservice. Der Code ist wesentlich aufgeräumter und leichter lesbar. Wenn ich jetzt noch die 10 Minuten einhalte, sollte nichts mehr schief gehen.

Bis jetzt ist alles noch recht einfach. Man sollte jedoch immer im Kopf behalten: kann ich meinen Code aus Effizienz Gründen „gleichzeitig“ ausführen oder ist eine sequentielle Reihenfolge notwendig?

Spaghetti kochen ist keine schwere Sache. In kochendes Wasser legen und die angegebene Zeit lang kochen. Schauen wir uns daher lieber die „bologneseKochen“ Funktion genauer an. Da müssen zwei Zwiebeln geschnitten, das Fleisch angebraten, Knoblauch gepresst, Dosen mit Tomatensoße geöffnet und alles zusammengemixt werden und schließlich wird alles noch gewürzt. Viele Handgriffe die ich nacheinander machen muss um meine perfekte Soße zu erhalten. Zuerst will ich die Zwiebel schneiden und anbraten, dann das Fleisch anbraten, den Knoblauch hinzufügen, würzen und am Schluss die Tomatensoße hinzufügen damit das Ganze dann langsam einkocht.

Wir halten aus Platzgründen die Methoden jetzt recht simpel.

Alle Funktionen fügen etwas der Zutaten-Liste hinzu.

Die Kochen Funktion ermittelt dann ob alle notwendigen Zutaten vorhanden sind und gibt dann aus ob das Kochen erfolgreich war oder eben nicht.

Oft würde der Code in etwa so aussehen:

Das Ganze ist schlecht lesbar. Eine Verschachtelung in einer Verschachtelung in einer Verschachtelung usw. Die Zeilen des Codes gehen immer tiefer hinein und machen auch den Fehlerfall unnötig kompliziert. Das ist wie wenn man sich beim Kochen nicht vorbereitet und die Küche nach dem Kochen einer Verwüstung gleicht: das Essen ist gekocht – aber die Küche endet in einem katastrophalen Zustand und man findet nichts mehr. Eine elegantere, kürzer und leichter lesbare Lösung ist diese:

Der Code ist gut lesbar, ich sehe auf einen Blick wann was passiert. Zusätzlich kann ich hier auf einen Blick erkennen, was im Fehlerfall passiert ohne tief verschachtelte Promises verfolgen zu müssen. Alle Fehler werden am Schluss abgehandelt mit einem einzelnen catch.

Wieso wird jedoch trotzdem die 1.Variante so oft benutzt? Ich glaube es liegt daran weil man grundsätzlich weiß wie man ein Promise schreibt. Ein zweites Promise löst man dann genau so. Wenn man jedoch weiß, dass es reicht die Funktion als ersten Paramter von „then“ aufzurufen und das Ergebnis dann weitergereicht wird, dann wird alles viel klarer und besser strukturiert.

Promises sind ein elegantes Werkzeug wenn sie richtig benutzt werden. Das Beispiel zeigt wie man einiges besser strukturieren kann. Wie die Küche, sollte man seinen Code sauber halten. Guten Appetit!

 

Schreiben Sie einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

*