Javascript async / await – erst mal warten

Skripte warten auf das Laden externer Ressourcen und auf Antworten vom Server, um weitere Aufgaben durchzuführen. async / await ist neben Callbacks und Promisses eine gut nachvollziehbare Technik zur Steuerung asynchroner Events. Das Besondere an async / await: Auch wenn die Ausführung asynchron abläuft, bleibt der Scriptcode organisiert und lesbarer.

async-await

setTimeout wartet nicht wie erwartet

Wenn wir einen Timeout einsetzen, stellen wir uns einen einfachen Ablauf vor. Ein Counter soll jeweils mit einem delay von einer Sekunde bis 5 zählen, dabei nach der "2" eine Pause (delay) von drei Sekunden einlegen.

timeout
await

Mit dem scheinbar gut verständlichen linearen Script funktioniert der Counter nicht so, wie wir uns das vorstellen: Der Zähler spring sofort auf 5: setTimeout läuft asynchron, die Zeilen unter den Aufrufen von delay werden sofort ausgeführt. JavaScript ist von Haus aus eine »nicht blockierende Sprache«: Anweisungen wie timeout blockieren die weitere Ausführung nicht.

Ohne async / await

const delay = seconds => {
	return new Promise (
		resolve => setTimeout (resolve, seconds * 1000)
	)
};

const countToFive = () => {
	console.log ("0 Sekunden");
	count.textContent = "0";
	 delay (1);
	console.log ("1 Sekunde");
	count.textContent = "1";
	 delay (1);
	console.log ("2 Sekunden");
	count.textContent = "2";
	 delay (3);
	console.log ("5 Sekunden");
	count.textContent = "5";
}
button.addEventListener ("click", () => {
	countToFive ();
})

async / await

const delay = seconds => {
	return new Promise (
		resolve => setTimeout (resolve, seconds * 1000)
	)
};

const countToFive = async () => {
	console.log ("0 Sekunden");
	count.textContent = "0";
	await delay (1);
	console.log ("1 Sekunde");
	count.textContent = "1";
	await delay (1);
	console.log ("2 Sekunden");
	count.textContent = "2";
	await delay (3);
	console.log ("5 Sekunden");
	count.textContent = "5";
}
button.addEventListener ("click", () => {
	countToFive ();
})

Das zweite Script mit den Aufrufen von async und await ist genauso linear geschrieben wie die erste Fassung. Der Unterschied liegt in await delay. Statt mit der nächsten Zeile fortzufahren, wartet das Script mit await auf die Ausführung des Requests und führt erst dann die nächste Anweisung aus.

Asynchron mit fetch

Mit fetch() liest JavaScript Dateien oder holt Informationen vom Server. fetch ist wie setTimeout von Haus aus asynchron.

const response = fetch ("https://api.com/values");
const json = response.json();
console.log (json);

Das funktioniert naturgemäß genauso wenig wie der Timeout ohne async/await, weil jede Anfrage über das Netz Zeit braucht, ganz gleich, ob sie auf eine externe Ressource oder selbst auf den gleichen Webspace zugreift.

Also nutzen wir then.

fetch ("https://api.com/values")
	.then (response => response.json())
	.then (json => console.log (json));

Das funktioniert, liest sich aber nicht so intuitiv wie die erste Fassung. Dafür steht heute in den modernen Browser await / async zur Verfügung. async / await ist besser lesbar und nachvollziehbar als Script-Code mit Promisses. Dabei erzeugen async und await unter dem Zuckergruß Promisses.

async als Teil der Deklaration einer Funktion gibt an, dass der Code asynchron ausgeführt werden soll. await vor einer Anweisung gibt ein Promise zurück, dass die Funktion anhält und auf das Ergebnis wartet, bevor die Ausführung fortgesetzt wird.

(async () => {
	const response = await fetch ("async-await.json");
	let json =  response.json();
	console.log (json);
})

await vor dem fetch führt dazu, dass der fetch-Request ausgeführt wird, bevor die nächste Zeile inAngriff genommen wird.

Ein etwas allgemeinerer Ansatz mit try – catch:

{ "list" : [
	{ "titel": "Eine kurze Geschichte von fast allem", "autor": "Bryson, Bill", "jahr": "2003" },
	{ "titel": "Accidental Empires", "autor": "Cringely, Robert X.", "jahr": "1992" },
	{ "titel": "Arm und Reich", "autor": "Diamond, Jared", "jahr": "1997" },
	{ "titel": "Bezaubernder April", "autor": "Elisabeth v. Armin", "jahr": "1992" }	
	]
}
const getBooks = async (num) => {
	try {
		let response = await fetch ("file.json");
		let json = await response.json ();
		let list = json.list;
		console.log (list[num].autor)
	} catch (e) {
		console.log ("Daten wurde nicht geladen", e);
	}
}

getBooks (2);

async-await-Funktionen wurden in Ecmascript 2017 (ES2017) vorgestellt und erlauben dem JavaScript-Interpreter, auf die Ausführung einer asynchronen Operation zu warten bevor er zur nächsten Zeile geht. Im Grunde genommen setzen async-await-Funktionen die nicht-blockierende Natur von Javascript innerhalb einer Funktion außer Kraft.

Asynchrone anonyme Funktionen

Davon können wir eine anonyme Funktion machen, die sofort dank der Schlüsselwörter async und await ohne den Aufruf getBooks (2) ausgeführt wird.

(async (num) => {
	try {
		let response = await fetch ("file.json");
		let json = await response.json ();
		let list = json.list;
		console.log (list[num].autor)
	} catch (e) {
		console.log ("Daten wurde nicht geladen", e);
	}
})(2);

Würden wir jetzt die Keywords async und await aus dem Script löschen, gäbe es eine Fehlermeldung in der Console.

Daten wurde nicht geladen
TypeError: response.json is not a function. (In 'response.json ()', 'response.json ' is undefined)
(anonyme Funktion)

response.json würde aufgerufen, bevor die Datei geladen wurde.

Suchen auf mediaevent.de