Newsletter            Wir rufen zurück            Impressum

JavaScript Promises (Anwendung)

Jeder UI5-Entwickler hat schonmal direkt oder indirekt die Erfahrung mit Promises gemacht. Dabei geht es um die Methoden create, read, update und remove eines UI5 OData-Models, welche für die Kommunikation mit dem Backend genutzt werden.

Meist wird in diesem Zusammenhang mit einem sog. callback gearbeitet: die Funktion, welche die Methoden des OData-Models ausführt, bekommt eine Funktion als Parameter, welche in einem der Zweige des Promise ausgeführt wird:

In dem Beispiel sind die ‚callback‘-Funktionen entsprechend ’successHandlerEventRead‘ und ‚errorHandlerEventRead‘ während das „PopupModel“ die relevanten Daten trägt.

Das Problem dabei ist die Teilung der Logik: Ein Teil der Logik kommt vor dem Aufruf, während der andere in der Callback-Funktion stattfindet, was die Übersicht stört. Praktischer wäre es stattdessen, wenn wir eine Notation zur Hand hätten, die uns asynchronen Code als synchronen Code schreiben lässt. Dazu müssen wir das Konzept des Promises mit einem anderen Kombinieren: sog. Generatoren. Generatoren sind Funktionen in Form iterierbarer Objekte, welche angehalten und dann wieder fortgesetzt werden können.

Das Vorgehen gestaltet sich folgendermaßen:

  1. In der Methode, welche auf das OData-Modul unserer App zugreift, muss eine Generator-Funktion definiert werden, welche die Logik einschließt.
  2. In der selben Methode muss die Ausführung der Generator-Funktion angestoßen werden.

Als Beispiel verwenden wir einen Backend-Aufruf, welcher die Daten zum Füllen einer Tabelle bereitstellt.

  1. Zunächst wird ein Prototyp des Generators erstellt, welcher zum aktuellen Closure zugewiesen wird (‚bind‘-Methode).
  2. Daraufhin wird das Generator-Objekt aufgerufen und mit Daten versorgt.
  3. Anschließend wird die Generator-Funktion angestoßen.
  4. ‚loadPersonenSet‘-Methode wird angestoßen und anschließend pausiert die Generator-Instanz in Zeile 30, bevor die Variable ‚data‘ einen Wert zugewiesen bekommen hat.

Die ‚loadPersonenSet‘-Methode liefert ein Promise, welches die Daten lädt.

Im ‚resolve‘-Zweig des Promise (success) können wir nun zurück in die ‚loadPersonenSet‘-Funktion springen, genauer gesagt in die Generator-Instanz, wo wir die Ausführung in Zeile 30 wieder aufnehmen, als wäre es synchroner Code:

  1. ‚data‘-Variable erhält einen Wert
  2. Ein Model wird erstellt und an die View weitergereicht.

Ein etwas unschöner Aspekt der Generator-Instanz ist, dass wir keinen Verweis auf die Instanz zur Verfügung haben und deswegen den Namen der Variable in Zeile 30 hart codieren müssen.

Eine andere Frage, die auftreten kann ist folgende: Warum mussten wir in Zeile 25 die ‚bind‘-Methode aufrufen ? Das hat etwas mit der asynchronen Ausführung zu tun. Wenn das Promise Ergebnisse liefert, ist die ‚loadPersonenSet‘-Funktion schon durchgelaufen und ihr Closure vom Garbage-Collector verworfen, d.h. dass wir keine Verknüpfung zum Modul mehr haben, in welchem sich die Funktion befindet und die Ergebnisse nicht in das Objekt einspeisen können. Um das zu verhindern, verknüpfen wir die Instanz des Generators mit dem Closure der übergeordneten Funktion, wodurch wir verhindern dass der Garbage-Collector ihn löscht. Das führt dazu dass wir nach der Rückkehr in das iterierbare Objekt immer noch Zugriff auf Funktionen wie getView() haben.

Sicherlich ist das ein einfaches Beispiel, welches jedoch helfen kann, das Coding zu strukturieren und an Relevanz gewinnen kann, wenn man mehrere asynchrone Aufrufe koordinieren muss.

Antwort schreiben