Blog der singularIT GmbH

  • Unsere Unternehmenswebseite
Search

KEY Talks #3 – Asynchrone Funktionen in Javascript

  • Veröffentlicht am 4. Juli 2018
  • Kategorie Allgemein

ECMAScript 2017 / ECMAScript 8 hat viele – insbesonders kleine – Neuerungen mit sich gebracht. Die wohl größte Änderung besteht in der Implementierung asynchroner Funktionen. Aber was genau sind diese asynchronen Funktionen? Bevor man diese Frage beantworten kann, benötigt man ein paar Grundlagen:

  • Wofür stehen Blocking / Non-Blocking?
  • Was versteht man unter Promise?
  • Was sind Generator Funktionen?

Blocking und Non-Blocking

Blocking heißt, dass unsere JS-Datei eine Pause einlegt und wartet, bis eine bestimmte nicht-JS Operation ausgeführt wurde. Der Event-Loop kann ohne Abschluss dieser externen Operationen nicht fortfahren. In JS ist Blocking ziemlich untypsch, da es viel zu ineffizient ist. Eine der wenigen und bekanntesten, blockenden Funktionalitäten ist alert(); Die Ausführung des JS-Codes geht erst dann weiter, wenn das Alert-Fenster geschlossen wurde.

Non-Blocking ist das Gegenteil vom Blocking. JS wartet nicht auf externe Operationen, sondern führt folgende Codestücke synchron aus. Dadurch wird das Script zwar um ein vielfaches schneller, aber es zieht auch Probleme mit sich. Eines dieser Probleme zeigt folgendes Beispiel:

var myData = loadData('/myFile');
console.log(myData);

>> undef

loadData versucht eine externe Datei einzulesen. Dies Prozess wird synchron ausgeführt! Da myData zum Zeitpunkt der Ausgabe noch nicht geladen ist, erhält man die Ausgabe >> undef.

Callbacks

Die Standardvariante, um dieses Problem zu umgehen sind Callbacks. Also Funktionen, die nach Abschluss der externen Operation ausgeführt werden. Ein Beispiel kann folgendermaßen aussehen:

loadData('/myFile', (data) => {
    console.log(data);
});

>> "Hello World"

Diese Variante ist ziemlich simpel, kann aber umständlich werden. Was passiert, wenn man mehrerer solcher externen Funktionen hintereinander ausführen muss und gleichzeitig auf alle angewiesen ist? Und wie geht man mit Errors um? Ist es dann immer noch so einfach?

var obj = {}
loadData('/myFile1', (data1, error1) => {
    obj['file1'] = data1
    loadData('/myFile2', (data2, error2) => {
        obj['file2'] = data2
        loadData('/myFile3', (data3, error3) => {
            obj['file3'] = data3
            console.log(obj);
        });
    });
});

>> {"file1" => "how", "file2" => "are", "file3" => "you"}

Was sehen wir? Man kann mehrere Callbacks verschachtelt.
Ist das schön? Nein!

Und dabei wurden hier noch nicht einmal Errors näher betrachtet! Natürlich hat man schnell gemerkt, dass Callbacks nicht das Gelbe vom Ei sind, darum hat man sich etwas Neues ausgedacht: Promises

Promises

Promises lassen sich wie normale Variablen einer Operation zuweisen. Jedoch werden sie nicht undefined, wenn die Operation noch nicht abgeschlossen wurde. Promises speichern, wie das fertige Objekt aussehen wird, sobald die Operation abgeschlossen ist. D.h.:

var promise = new Promise(function(resolve) {
    resolve(loadData('/data1'));
});
console.log(promise);

>> Promise { "pending" }

Um das Promise auszuführen,  nutzt man den then-Befehl:

promise.then(function(data) {
    console.log(data);
});

Das sieht ja fast wie ein normales Callback aus! Also wozu das alles? Ganz einfach! Promises haben zwei entscheidende Vorteile:

  • Chaining wird ermöglicht
  • Error Handling wird einfacher

Um nun unsere 3 externen Funktionen zu verketten und gleichzeitig nach Errors zu schauen, schreibt man:

let obj = {};
loadData('/data1').then((data1) => {
    obj['file1'] = data1;
    return(loadData('/data2'));
}).then((data2) => {
    obj['file2'] = data2;
    return(loadData('/data3'));
}).then((data3) => {
    obj['file3'] = data3;
    console.log(obj);
}).catch(function (e) {
    console.err("ERROR!");
});

>> {"file1" => "how", "file2" => "are", "file3" => "you"}

Der Code wird also nicht nur übersichtlicher. Man kann sich auch um alle Errors innerhalb der Promises kümmern.
Aber irgendwie ist auch das nicht zufriedenstellend. Möchte man in diesem Codeschnippsel sequenzielle Operationen (if/else, for/while, …) einbringen, gestaltet sich das als problematisch. Sequenzielle Programmierung erlauben Promises nicht. Also die Frage: Geht es nicht irgendwie besser? – Schauen wir uns einen anderen Ansatz an: Generator Funktionen

Generator Funktionen

Auf dem ersten Blick sehen Generator Funktionen wie jede andere Funktion aus. Jedoch ermöglichen sie Pausen beim Ausführen von Operationen. Eine Funktion könnte wie folgt aussehen:

function* myGenerator() {
    const obj = yield loadData('/data1');
    console.log(obj);
}

Die Funktion zeichnet sich durch das Sternchen zu Beginn und dem Keyword yield aus. Die Funktion geht bis zum yield und wartet, bis das dazugehörige Promise ausgeführt wurde. Erst dann geht es weiter. Das klingt praktisch, aber so einfach ist es leider nicht: Generator Funktionen brauchen Hilfsfunktionen. Diese verarbeiten das Promise und geben der Generator Funktion Bescheid, wenn sie weiterarbeiten darf:

const iterator = myGenerator();
const iteration = iterator.next();

iteration.value.then(resolvedValue => {
    iterator.next(resolvedValue);
});

Die Generator Funktion entpuppt sich als ein Iterator, der über jedes yield einzeln iteriert. Die gewonnen Informationen werden der Generator Funktion returnt. Es zeigt sich, dass Generator Funktionen auch nicht wirklich die Spitze des Eisberges darstellen.

Und genau aus diesem Grund kommen asynchrone Funktionen ins Spiel!

Asynchrone Funktionen

Asynchrone Funktionen in JS kann man sich als Kombination aus Promises und Generator Funktionen vorstellen. Sie verbinden das Chaining und Error Handling der Promises mit der Möglichkeit, innerhalb einer Funktion zu pausieren. Dadurch wird auch Sequenzielle Programmierung wieder möglich! Aber genug der Worte, wie sehen diese „magischen“ Funktionen nun aus?!

asynch function getData(){
    let result = await loadData('/data1');
    console.log(result);
}

Das sieht fast wie eine Generator Funktion ohne Iterator aus, oder? Exakt! Genau das macht die Asynchronen Funktionen aus. Sie brauchen keine Hilfsfunktionen um Promises auszuführen, sondern führen sie alle sequenziell und vollkommen automatisch aus. Wie würde es nun aussehen, wenn wir wieder unsere drei Dateien laden und den Inhalt speichern wollen?

async function test() {
    let obj = {};
    try{
        let data1 = await loadData('/data1');
        let data2 = await loadData('/data2');
        let data3 = await loadData('/data3');

        obj['file1'] = data1;
        obj['file2'] = data2;
        obj['file3'] = data3;

        console.log(obj)
    }catch(e){
        console.err('Error ist aufgetreten: ' + e);
    }
}

>> {"file1" => "how", "file2" => "are", "file3" => "you"}

Ziemlich simpel, oder? Wie man sehen kann, wurde für das Error Handling einfach ein try-catch-Block eingebunden. So, wie man es kennt, ohne irgendwelche Anweisungen kompliziert zu verschachteln.

Mit dieser Struktur kann man programmieren, wie man es kennt: sequenziell und einfach. Auch Schleifen oder If-Bedingungen stellen kein Problem mehr dar.

Kommentare sind geschlossen

Neueste Beiträge
  • Girls Day bei der singularIT
  • Semesterauftakt-Kickern und -Billard
  • Fridays Bar bei singularIT
  • Weihnachtsfeier 2022
  • Der Gesundheitstag 2022 @ singularIT
Archive
  • Mai 2023
  • April 2023
  • Januar 2023
  • Dezember 2022
  • November 2022
  • Oktober 2022
  • August 2022
  • Juli 2022
  • Mai 2022
  • Februar 2022
  • Dezember 2021
  • November 2021
  • Oktober 2021
  • September 2021
  • August 2021
  • Juli 2021
  • Mai 2021
  • Februar 2021
  • Januar 2021
  • Dezember 2020
  • November 2020
  • Oktober 2020
  • September 2020
  • Juli 2020
  • März 2020
  • September 2019
  • Juni 2019
  • Mai 2019
  • Dezember 2018
  • November 2018
  • Juli 2018
  • Mai 2018
  • April 2018
  • Januar 2018
  • Dezember 2017
  • Oktober 2017
  • September 2017
  • Juni 2017
  • November 2016
  • Oktober 2016
  • Juni 2016
  • Mai 2016
  • April 2016
  • März 2016
Kategorien
  • Allgemein
  • Deep Learning
  • Java
  • PHP
  • Über uns
  • Webprogrammierung
  • Datenschutz
  • Impressum
Copyright © 2023 Blog der singularIT GmbH All Right Reserved.
designed byBest Press Theme