Javascript async / await

async-await

async / await ist neben Callbacks und Promisses eine intuitive und gut nachvollziehbare Technik zur Steuerung asynchroner Events. Das Besondere an async / await: Auch wenn die Ausführung asynchron abläuft, bleibt der Scriptcode besser organisiert und lesbarer.

23-02-02 SITEMAP CSS HTML JS Basis JS Web Tutorial SVG

Wenn setTimeout nicht wartet

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.

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

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);

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.