Hallo borisbaer,
Aber wie komme ich an den Wert von <value> heran, um ihn dann zu verarbeiten?
Promises müssen verkettet werden. Hier mal ohne viel Fehlerbehandlung:
function checkUser(name) {
return fetch("/foo/"+encodeUriComponent(name)) // liefert Promise 1
.then(response=>response.json()) // liefert Promise 2
.then(obj=>obj.username); // liefert Promise 3
}
Was man wissen muss, ist, dass nicht nur fetch ein Promise zurückgibt. Es gibt noch viel mehr. fetch liefert das Promise 1. Die Ausführung dauert etwas, darum ist das pending. Auf diesem Promise 1 rufst Du .then auf. Dieses .then liefert Dir ein Promise 2, und auf diesem Promise 2 rufst Du den nächsten .then auf. Auch DER liefert wieder ein Promise und DAS gibst Du aus getData zurück. Lies Dir mal das hier durch.
Sobald fetch die Responseheader gelesen hat, resolved das Promise 1. In Folge wird der Callback des ersten .then aufgerufen. Der ruft response.json() auf und gibt das Ergebnis zurück. ABER die json() Methode gibt selbst auch ein Promise zurück, das Promise 4. Denn sie muss erstmal I/O machen, fetch hat nur die Header gelesen und die Daten sind noch auf der Leitung.
Wenn Du meinen Link nachgelesen hast, dann weißt Du, dass nun Promise 4 und 2 gekoppelt sind. Sobald json() fertig ist, resolved es Promise 4, damit Promise 2 und der Callback des zweiten .then läuft los.
Der liefert einen String und resolved damit Promise 3.
Das alles passiert aber erst, nachdem getData() schon läääängst zurückgekehrt ist. Der JavaScript-Task, der getData() aufgerufen hat, ist nicht mehr da. Er kann nicht mehr da sein, denn die Promise-Callbacks laufen in der Microtask-Queue (siehe Link!) und die startet erst, wenn der Haupttask durch ist.
Du kommst an den Wert also nur heran, wenn Du Dich der Promise-Logik unterwirfst. Du bekommst von checkUser das Promise 3 zurück. Aber in dem Moment, wo es zurückkommt, hat es noch keinen Wert. Du musst Dich also selbst an das Versprechen ranhängen. Mit .then():
checkUser("borisbaer")
.then(function(username) {
// Ist einer gekommen, dann Kollision melden
});
Diese ganze Callbäckerei ist natürlich lästig und deshalb hat man nach dem Konditor gerufen, damit er Zuckerguss draufmacht und async/await bekommen.
await wartet darauf, dass ein Promise sich auf ein Ergebnis festlegt. Unter der Haube ist das aber immer noch nichts anderes als ein .then-Aufruf und alles, was hinter await in deiner Funktion steht, muss in den .then-Callback hinein. Die JavaScript-Engine baut also deinen Code für Dich um.
Eine Konsequenz davon ist, dass deim Timing kaputt geht. Du schreibst:
function checkUser(name) {
let response = await fetch("/foo/"+name);
if (response.ok) {
let user = await response.json();
return user;
}
}
und siehst gar nicht mehr, dass die checkUser-Funktion nach dem ersten await erstmal fertig ist und der Rest in Microtasks abläuft. Um noch eine Ablaufreihenfolge hinzubekommen, muss checkUser ein Promise zurückgeben, und dafür dient async. Das async-Schlüsselwort sagt, dass diese Funktion ein Promise zurückgibt. Und das tut sich auch, selbst wenn Du nichts asynchrones darin machst. Wenn Du in einer async-Funktion return xy;
programmierst, entspricht das einem resolve(xy);
Aufruf. Endest Du ohne return, entspricht das einem resolve(undefined). Und wenn Du mit throw etwas wirfst, entspricht das dem Aufruf von reject.
Heißt also: Du musst ENTWEDER das Ergebnis von checkUser awaiten, was Dir dann auferlegt, dass der Aufrufer von checkUser selbst wieder async sein muss. Das ist das Blöde an async, es ist ansteckend und bahnt sich seinen Weg bis ganz nach oben.
In neueren JavaScript-Versionen ist ein "top level await" zulässig geworden. Das war es früher nicht, da MUSSTE man eine async-Funktion haben, um await machen zu können.
Die Alternative zum await ist, einfach das Promise aus der async-Funktion als Promise zu nehmen und .then darauf aufzurufen.
Rolf
sumpsi - posui - obstruxi