© istockphoto.com/David Marchal
Wie Sie jetzt schon Async Functions in Ihrem Code implementieren

Asynchrones JavaScript


Asynchronität in JavaScript-Applikationen sorgt häufig für Kopfzerbrechen bei Entwicklern. Der Lesefluss des Quellcodes wird unterbrochen. Die Verständlichkeit leidet. Spätestens wenn Abhängigkeiten zwischen mehreren asynchronen Operationen ins Spiel kommen, benötigen Sie Werkzeuge, die Ihnen bei der Lösung Ihrer Probleme helfen. Im Verlauf dieses Artikels lernen Sie verschiedene Strategien zum Umgang mit asynchronen Operationen und der asynchronen Flusssteuerung kennen. Außerdem wagen wir einen Blick in die Zukunft.

Bevor Sie sich in die Behandlung asynchroner Programm­abläufe stürzen, sollten Sie zunächst verstehen, was Asynchronität bedeutet, welche Probleme Sie damit lösen können und welche Herausforderungen sich daraus ergeben. Beginnen wir mit einem Beispiel, um die Problemstellung etwas besser zu verdeutlichen. Bestimmt kennen Sie das XMLHttpRequest-Objekt Ihres Browsers, mit dem Sie aus Ihrer Frontend-Applikation heraus asynchrone Anfragen an einen Webserver erstellen können. Diese Methode ist besser unter dem Namen Ajax bekannt. Das wichtigste Merkmal von Ajax versteckt sich hinter dem ersten Buchstaben dieses Akronyms. Dieser steht für asynchronous. Sie formulieren die Anfrage an den Server und registrieren eine oder mehrere Callback-Funktionen, die das Ergebnis dann behandeln sollen. Danach wird die Anfrage abgesendet und Ihre Applikation läuft ganz normal weiter und kann mit Eingaben des Benutzers umgehen. Sobald die Antwort des Servers vorliegt, werden die entsprechenden Callback-Funktionen ausgeführt. Während des gesamten Vorgangs ist Ihre Applikation weiterhin responsiv, die Anfrage an den Server blockiert also nichts in Ihrer Applikation.

Sie haben natürlich auch die Möglichkeit, einen solchen XMLHttpRequest, auch bekannt als XHR, synchron abzusetzen. Dann wartet Ihr Browser jedoch auf die Antwort des Servers und führt kein weiteres JavaScript in der Zwischenzeit aus. Ihre Applikation ist in diesem Fall blockiert.

Der Event-Loop

Asynchronität ist stark mit Ereignissen verbunden. Das Konzept lässt sich recht einfach erklären: Wenn Sie eine Callback-Funktion registrieren, wird sie in eine Warteschlange eingereiht. Sobald dann die Bedingung eintritt, für die die Callback-Funktion registriert wurde, wird sie ausgeführt. Um die korrekte Abarbeitung dieser Warteschlange kümmert sich der Event-Loop des Browsers. Er sorgt dafür, dass alle Callback-Funktionen bis zu ihrem Ende abgearbeitet werden.

In JavaScript sehen Sie sich wesentlich häufiger mit Asynchronität konfrontiert als nur bei asynchronen Anfragen an ein Backend. Auch die Reaktionen auf einen Klick auf ein Button-Element sind schon asynchron. Ein weiterer Kandidat für Asynchronität sind zeitabhängige Routinen, die Sie mit setTimeout oder setInterval erstellen. Schon ein setTimeout mit einer Verzögerung von 0 Millisekunden führt dazu, dass die Callback-Funktion in die Warteschlange des Event-Loops eingeordnet und im nächsten Durchlauf der Schleife sofort abgearbeitet wird. Ein klassischer Fall von Asynchronität.

Nachteile von Asynchronität

Abseits vom schönen Nebeneffekt, dass asynchrone Operationen Ihre Applikation nicht blockieren und sich damit sehr responsive Frontends umsetzen lassen, gibt es ein paar Schattenseiten, die Sie sich mit dieser Art der Programmierung erkaufen:

  • Eingeschränkter Lesefluss: Sobald Sie asynchron programmieren, müssen Sie sich von einem durchgängigen Lesefluss innerhalb Ihrer Applikation verabschieden. Das macht das Verstehen des Quellcodes etwas schwieriger.

  • Verlassen des Kontexts: Wenn die Callback-Funktion in Ihrer Applikation abgearbeitet wird, befinden Sie sich standardmäßig nicht mehr im umliegenden Kontext. Ihre asynchrone Operation hat keinen Rückgabewert, mit dem Sie direkt arbeiten können, da die Abarbeitung ja verzögert erfolgt.

  • Unübersichtlich bei verschachtelten Operationen: Haben Sie den Fall, dass zwei Ihrer asynchronen Operationen voneinander abhängen, weil beispielsweise die zweite Operation mit dem Ergebnis der ersten arbeiten muss, spricht man von einer verschachtelten Operation, da die zweite Anfrage innerhalb der Callback-Funktion der ersten ausgeführt wird. Gehen Sie nun davon aus, das Sie nicht nur zwei, sondern viel mehr voneinander abhängige asynchrone Operationen haben, befinden Sie sich in der so genannten Callback-Hölle, einer Kaskade von asynchronen Operationen, die alle irgendwie voneinander abhängen.

  • Keine Kontrolle, wann die Funktion ausgeführt wird: Bei einer asynchronen Operation wie einem XHR können Sie nie sicher sein, wann genau Ihre Callback-Funktion aufgerufen wird. Je nach Netzwerklatenz kann es zu Verzögerungen von mehreren Sekunden kommen. Und nicht einmal die zeitabhängigen Funktionen wie setTimeout sind wirklich zuverlässig. Sie können zwar einen Wert in Millisekunden angeben, ob der Event-Loop und Ihre JavaScript Engine die Callback-Funktion wirklich in genau der gewünschten Millisekunde ausführen, ist mehr als fraglich.

Wenn asynchrone Programmierung doch so viele Probleme verursacht, warum verabschiedet man sich nicht davon und setzt eine Applikation einfach synchron um? Die Antwort ist einfach: In den meisten Fällen ist es keine Option, alles synchron umzusetzen. Ein Benutzer Ihrer Applikation würde mit einer nicht zeitgemäßen Oberfläche arbeiten müssen und sehr häufig auf die Antwort Ihres Systems warten. Es gibt Einzelfälle, in denen Sie Ihren Benutzern eine solche Applikation aufzwingen können. In den meisten Fällen verlieren Sie jedoch sehr schnell Ihre Benutzer mit einer blockierenden Applikation, deren herausragendstes Feature lange Wartezeiten sind.

Glücklicherweise gibt es mittlerweile jede Menge Hilfsmittel, die Sie dabei unterstützen, asynchrone Operationen in JavaScript in den Griff zu bekommen und die bereits erwähnten Nachteile entweder ganz zu beseitigen oder zumindest spürbar zu mildern.

async

Eine Bibliothek, die zwar eigentlich für Node.js gedacht war, mittlerweile aber auch recht häufig in browserseitigem JavaScript eingesetzt wird, ist async [1]. Diese Bibliothek wurde Anfang 2010 ins Leben gerufen und wird seitdem aktiv weiterentwickelt. Die Funktionalität der Bibliothek lässt sich grob in die zwei Bereiche „Unterstützung funktionaler Programmierung“ und „asynchrone Flusskontrolle“ unterteilen. Wobei der zweite Bereich für diesen Artikel der interessantere ist.

Installieren können Sie async entweder über Bower, oder Sie laden sich die Bibliothek direkt von GitHub herunter. Danach müssen Sie async lediglich noch mittels script-Tag einbinden und können es verwenden.

async stellt Ihnen unter anderem Methoden wie parallel oder series zur Verfügung, mit denen Sie asynchrone Aufgaben parallel beziehungsweise hintereinander ausführen können. Zu diesem Zweck müssen die asynchronen Tasks, mit denen die Bibliothek arbeitet, einem bestimmten Format folgen. Listing 1 e...

Neugierig geworden? Wir haben diese Angebote für dich:

Angebote für Gewinner-Teams

Wir bieten Lizenz-Lösungen für Teams jeder Größe: Finden Sie heraus, welche Lösung am besten zu Ihnen passt.

Das Library-Modell:
IP-Zugang

Das Company-Modell:
Domain-Zugang