Javascript Promise

Promise ist eine neue Technik, die mit ECMAScript 6 eingezogen ist und den Umgang mit asynchronem Verhalten sicherer und durch die then-Funktion lesbarer machen. Asynchron bedeutet, dass zwischen einer Anfrage und einer Antwort eine Verzögerung oder Wartezeit liegt.

18-12-15 SITEMAP

Callbacks bei zeitlich unbestimmbaren Abläufen

Wenn wir auf einen zeitlich nicht absehbaren Ablauf angewiesen sind – z.B. beim Laden eines Bildes oder einer json-Datei – werden Callbacks eingesetzt, die beim Eintreten eines Ereignisses aufgerufen werden.

Da die Callback-Funktion i.d.R. nur einmal aufgerufen werden, sehen wir Callbacks oft als anonyme Funktion. Promises ergänzen Callbacks, geben dem Code eine besser lesbare Struktur und zusätzliche Garantien, die den asynchronen Ablauf verläßlicher machen.

Asynchrone Events und das Warten

Wenn Daten geladen werden, dauert das Warten immer eine unbestimmte Zeit. Wenn nur ein Ladevorgang beobachtet werden muss, ist alles noch einfach. Aber was ist, wenn nach dem Laden der JSON-Datei auf eine weitere Aktion gewartet werden muss, z.B. weil die URL der zweiten JSON-Datei in der ersten URL steht?

const url = "file.json";
const xhr = new XMLHttpRequest(url);
xhr.addEventListener ("readystatechange", (url) => {
	if (xhr.readyState === 4) {
		console.log (xhr.responseText);
	}
})
xhr.open ("GET", url);
xhr.send();
const url = "../fetch.json";
const url2 = "../file.json";
const url3 = "../data.json";
const xhr = new XMLHttpRequest(url);
xhr.addEventListener ("readystatechange", (url) => {
	if (xhr.readyState === 4) {
		///console.log ("xhr " + xhr.responseText);
		
		const xhr2 = new XMLHttpRequest(url2);
		xhr2.addEventListener ("readystatechange", (url2) => {
		if (xhr2.readyState === 4) {
			//console.log ("xhr2 " + xhr2.responseText);
			
			const xhr3 = new XMLHttpRequest(url3);
			xhr3.addEventListener ("readystatechange", (url3) => {
				//console.log ("xhr3 " + xhr3.responseText);
			});
			xhr3.open ("GET", url3);
			xhr3.send();
		}
		
	});
	xhr2.open ("GET", url2);
	xhr2.send();
}});
xhr.open ("GET", url);
xhr.send();

Javascript Promise

Mit verschachtelten asynchronen Events kommen wir in Teufels Küche, die auch als Callback Hell bezeichnet wird. Auch jQuery und Javascript fetch würden dieses abgrundtiefe Loch verschachtelter asynchroner Aufrufe nicht wirklich vereinfachen.

Promises sind Objekte, die das Ergebnis einer asynchronen Aktion abfangen und es zurück geben, wenn die versprochenen Daten erfolgreich geladen wurden oder ein Fehler aufgetreten ist.

Promises sind eine Schutzmaßnahme. Sie werden niemals vor dem Ende des Wartens auf ein Event aufgerufen und sie werden nur einmal ausgeführt. Gleich ob ein Promise erfüllt werden kann oder nicht, ruft es auf jeden Fall die korrekte Callback-Funktion auf. Darüber hinaus können Callbacks miteinander verketted (chained) werden: Dann ruft das Ergebnis einer Operation am Ende die nächste Operation auf.

Promise und then ()

Ein einfacher XMLHttpRequest mit nur einem asynchronen Request ist übersichtlich, aber sobald ein weiterer Request auf dem ersten Request beruht, wird der Code verschachtelt und unübersichtlich.

Promises stellen eine Methode then () zur Verfügung, die ausgeführt wird, nachdem das Versprechen eingelöst wurde. then () enthält zwei optionale Argumente: ein Callback für den Erfolg, ein Callback für den Fehlerfall.

So kann z.B. eine komplizierte Aktionsfolge aussehen, bei der zwei JSON-Dateien geladen werden sollen, wobei das Auslesen der zweiten JSON-Datei nur Sinn macht, wenn das erste JSON korrekt angeliefert wurde.

fetch (url).then ( function (response) {
	return response.json();
}).then ( function (data) {
	console.log ("url 1 " + data)
}).then (fetch (url2).then (function (response) {
	return response.json();
}).then (function (data) {
	console.log ("url 2 " + data)
}).then (fetch (url3).then (function (response) {
	return response.json();
}).then (function (data) {
	console.log ("url 3 " + data)
})
))

Da haben wir ein einfaches Muster: then gefolgt von einem return, then gefolgt von einem return.

then-Aufrufe können sequentiell aneinander gehangen werden, um zusätzliche asynchrone Aktionen nacheinander auszuführen (chaining).