Im heutigen Web sind asynchrone Operationen allgegenwärtig. Wir laden Daten von einem Server, warten auf Benutzereingaben oder setzen Timer. All diese Operationen können in JavaScript und TypeScript auf asynchrone Weise gehandhabt werden.
Bisher wurden Callback-Funktionen und Promises verwendet, um
asynchronen Code zu schreiben. Diese Techniken können jedoch in
komplexen Szenarien schwer zu verstehen und zu handhaben sein. Deshalb
wurden async und await eingeführt. Sie sind
eine Erweiterung von Promises und machen den asynchronen Code lesbarer
und einfacher zu schreiben.
Eine Funktion, die mit dem Schlüsselwort async
deklariert wird, gibt immer ein Promise zurück. Wenn das Resultat einer
async Funktion ein Wert ist, wird der Wert in ein
Promise-Objekt verpackt und zurückgegeben.
async function myFunction() {
return "Hello World";
}
console.log(myFunction());Innerhalb einer async Funktion können wir das
await Schlüsselwort verwenden, um die Ausführung der
Funktion zu pausieren, bis das Promise erfüllt ist. Dies führt zu einem
synchron aussehenden Code, der eigentlich asynchron ist.
async function myFunction() {
let value = await fetchFromServer();
console.log(value);
}In diesem Beispiel wird myFunction nicht weiter
ausgeführt, bis fetchFromServer() abgeschlossen ist.
Die Funktion, die ein Promise zurückgibt, wird asynchron
ausgeführt, was bedeutet, dass sie im Hintergrund abläuft und der Code
danach weiter ausgeführt wird. Sie müssen das Promise nicht
unbedingt “abholen”. Wenn Sie jedoch das Ergebnis der asynchronen
Operation verwenden oder auf ihren Abschluss warten möchten, müssen Sie
das Promise abholen.
Zum Beispiel, wenn Sie eine Funktion haben, die ein
Promise zurückgibt, und Sie den Rest Ihres Codes ausführen
möchten, ohne auf das Ergebnis zu warten, können Sie einfach die
Funktion aufrufen und ihr Ergebnis ignorieren:
// ruft die Funktion auf, wartet aber nicht auf ihr Ergebnis
asyncFunction();
// wird sofort ausgeführt, ohne auf asyncFunction zu warten
doOtherStuff();Aber wenn Sie das Ergebnis verwenden oder auf den Abschluss der
asynchronen Funktion warten müssen, dann sollten Sie das
Promise mit await abholen oder die
then Methode verwenden:
// Mit await
async function main() {
// wartet auf das Ergebnis
const result = await asyncFunction();
// verwendet das Ergebnis
doOtherStuff(result);
}
main();
// Mit then
asyncFunction()
// wird ausgeführt, wenn das Promise erfüllt ist
.then(result => doOtherStuff(result));Bitte beachten Sie, dass await nur innerhalb von
async Funktionen verwendet werden kann. Es ist auch wichtig
zu beachten, dass, obwohl Sie nicht auf ein Promise warten
müssen, unerledigte Promises, die abgelehnt werden (z.B. aufgrund eines
Fehlers in der asynchronen Operation), einen
UnhandledPromiseRejectionWarning in Node.js auslösen
können. Daher ist es immer eine gute Praxis, Promises zu behandeln und
geeignete Fehlerbehandlungsmechanismen bereitzustellen.
Wir können try/catch-Blöcke verwenden, um Fehler in
async/await abzufangen. Wenn ein Fehler in der
fetchFromServer Funktion auftritt, wird er durch den
catch-Block abgefangen.
async function myFunction() {
try {
let value = await fetchFromServer();
console.log(value);
} catch (error) {
console.log(error);
}
}Mit Promise.all() können wir mehrere Promises
gleichzeitig ausführen und auf alle warten, bevor wir fortfahren.
async function myFunction() {
let [value1, value2] = await Promise.all([fetchFromServer1(), fetchFromServer2()]);
console.log(value1, value2);
}In diesem Beispiel warten wir, bis beide
fetchFromServer1 und fetchFromServer2
abgeschlossen sind, bevor wir fortfahren.
Async und await sind mächtige Werkzeuge,
die uns helfen, asynchronen Code in einer Weise zu schreiben, die
leichter zu verstehen und zu verwalten ist. Sie sind eine wichtige
Ergänzung zu den vorhandenen Techniken zur Behandlung von Asynchronität
in JavaScript und TypeScript. Mit ihnen können wir klarere und weniger
fehleranfällige asynchrone Logik schreiben.
Promises sind ein zentraler Baustein moderner asynchroner
Programmierung in JavaScript und TypeScript. Mit den Methoden
then(), catch() und finally()
kann man Aktionen planen, die nach dem Erfolg oder Fehlschlagen eines
Promises ausgeführt werden sollen.
Die then() Methode wird verwendet, um Callbacks
anzuhängen, die ausgeführt werden, wenn das Promise erfüllt (erfolgreich
abgeschlossen) wird. Die Methode akzeptiert zwei Argumente, beide
optional: eine Callback-Funktion für den Erfolgsfall und eine für den
Fehlerfall.
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve("Promise erfüllt!"), 1000);
});
promise.then(
result => console.log(result), // "Promise erfüllt!"
error => console.log(error) // Wird nicht ausgeführt
);Die catch() Methode ist eine verkürzte Form von
then(null, errorHandler). Sie wird aufgerufen, wenn das
Promise abgelehnt (ein Fehler aufgetreten) wird.
let promise = new Promise((resolve, reject) => {
setTimeout(() => reject(new Error("Promise abgelehnt!")), 1000);
});
promise.catch(
error => console.log(error) // Error: Promise abgelehnt!
);Die finally() Methode ist ähnlich wie
then() und catch(), wird aber unabhängig vom
Erfolg oder Misserfolg des Promises ausgeführt. Es ist nützlich für die
Bereinigung von Ressourcen oder die Durchführung von Aktionen, die in
jedem Fall stattfinden sollten.
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve("Promise erfüllt!"), 1000);
});
promise
.then(result => console.log(result)) // "Promise erfüllt!"
.finally(() => console.log("Promise abgeschlossen."));In diesem Beispiel wird die finally() Methode
ausgeführt, nachdem das Promise erfüllt wurde und die
then() Methode ihre Arbeit abgeschlossen hat. Wenn das
Promise abgelehnt worden wäre, wäre finally() trotzdem
aufgerufen worden.
Hinweis: Während then() und catch() ein
neues Promise zurückgeben (das potenziell ein anderes Ergebnis oder
einen anderen Zustand hat), gibt finally() immer das
ursprüngliche Promise zurück.
Die Funktionen then, catch und
finally sowie async/await sind
beide Mechanismen zur Arbeit mit asynchronem Code in JavaScript und
TypeScript, speziell zur Arbeit mit Promises. Während then,
catch und finally Methoden sind, die direkt an
einem Promise aufgerufen werden, ist
async/await ein syntaktisches Konstrukt, das
die Arbeit mit Promises vereinfacht.
Hier sind einige Schlüsselunterschiede und Zusammenhänge zwischen ihnen:
then, catch und finally sind
Methodenaufrufe, die auf Promises angewendet werden. Sie nehmen
Callback-Funktionen als Argumente, die ausgeführt werden, wenn das
Promise erfüllt oder abgelehnt wird.
doSomethingAsync()
.then(result => console.log(result))
.catch(error => console.log(error))
.finally(() => console.log('Fertig'));async/await hingegen ist ein syntaktisches
Konstrukt, das es ermöglicht, asynchronen Code zu schreiben, der fast
wie synchroner Code aussieht. Ein await-Ausdruck pausiert
die Ausführung der Funktion, bis das Promise erfüllt ist, und gibt dann
den Wert des Promises zurück.
async function doSomething() {
try {
const result = await doSomethingAsync();
console.log(result);
} catch (error) {
console.log(error);
} finally {
console.log('Fertig');
}
}then, catch und finally können
in jedem Kontext verwendet werden, in dem Sie mit Promises arbeiten. Sie
sind universell und funktionieren in jeder Funktion und jedem
Kontext.
async/await kann jedoch nur in Funktionen
verwendet werden, die mit dem async-Schlüsselwort
gekennzeichnet sind. Es ist leistungsfähiger und flexibler, aber seine
Verwendung ist auf async-Funktionen beschränkt.
Mit then und catch können Sie die Erfolgs-
und Fehlerfälle jeweils getrennt behandeln. Mit
async/await hingegen können Sie die
Fehlerbehandlung mit traditionellen
try/catch-Konstrukten durchführen, was oft
intuitiver und übersichtlicher ist.
Insgesamt sind async/await und
then/catch/finally eng
miteinander verbunden und dienen dem gleichen Zweck: Die Arbeit mit
asynchronem Code und Promises. Welche Sie verwenden, hängt von Ihrem
speziellen Anwendungsfall und Ihrer bevorzugten Programmierstil ab.
async und await:Aufgabe 1: Schreiben Sie eine async Funktion namens
getRandomNumber, die nach 2 Sekunden ein zufälliges Zahl
zwischen 1 und 10 zurückgibt. Verwenden Sie dazu das
setTimeout Funktion und Promises.
Aufgabe 2: Schreiben Sie eine async Funktion namens
fetchUser, die die Daten eines Benutzers von der
JSONPlaceholder API (https://jsonplaceholder.typicode.com/users/1)
abruft. Verwenden Sie das fetch Funktion und
await, um auf die Antwort zu warten. Geben Sie das Ergebnis
als JSON aus.
Aufgabe 3: Erweitern Sie die fetchUser Funktion aus
Aufgabe 2 mit Fehlerbehandlung. Wenn ein Fehler auftritt, geben Sie “Ein
Fehler ist aufgetreten” in der Konsole aus.
Aufgabe 4: Schreiben Sie eine async Funktion namens
fetchAllUsers, die die Daten aller Benutzer von der
JSONPlaceholder API (https://jsonplaceholder.typicode.com/users) abruft.
Verwenden Sie Promise.all, um alle Anfragen parallel zu
bearbeiten. Geben Sie das Ergebnis als JSON aus.
Aufgabe 5: Schreiben Sie eine async Funktion namens
printUserNames, die die fetchAllUsers Funktion
verwendet, um alle Benutzerdaten abzurufen, und dann nur die Namen der
Benutzer ausgibt.
Lösung Aufgabe 1:
async function getRandomNumber() {
return new Promise<number>((resolve) => {
setTimeout(() => {
resolve(Math.floor(Math.random() * 10) + 1);
}, 2000);
});
}
// logs a random number between 1 and 10 after 2 seconds
getRandomNumber().then(console.log);Lösung Aufgabe 2:
async function fetchUser() {
const response =
await fetch('https://jsonplaceholder.typicode.com/users/1');
const user = await response.json();
console.log(user);
}
fetchUser();Lösung Aufgabe 3:
async function fetchUser() {
try {
const response =
await fetch('https://jsonplaceholder.typicode.com/users/1');
const user = await response.json();
console.log(user);
} catch (error) {
console.error('Ein Fehler ist aufgetreten');
}
}
fetchUser();Lösung Aufgabe 4:
async function fetchAllUsers() {
const response =
await fetch('https://jsonplaceholder.typicode.com/users');
const users = await response.json();
console.log(users);
}
fetchAllUsers();Lösung Aufgabe 5:
async function fetchAllUsers() {
const response =
await fetch('https://jsonplaceholder.typicode.com/users');
return response.json();
}
async function printUserNames() {
const users = await fetchAllUsers();
users.forEach(user => console.log(user.name));
}
printUserNames();then, catch und finally:Aufgabe 1: Erstellen Sie eine Funktion, die ein
Promise zurückgibt, das nach einer Verzögerung von 2 Sekunden erfüllt
wird. Verwenden Sie die then-Methode, um eine Meldung an
die Konsole zu senden, wenn das Promise erfüllt wird.
Aufgabe 2: Erstellen Sie eine Funktion, die ein
Promise zurückgibt, das nach einer Verzögerung von 3 Sekunden abgelehnt
wird. Verwenden Sie die catch-Methode, um eine
Fehlermeldung an die Konsole zu senden, wenn das Promise abgelehnt
wird.
Aufgabe 3: Kombinieren Sie die Funktionen aus den
Aufgaben 1 und 2. Verwenden Sie die finally-Methode, um
eine Meldung an die Konsole zu senden, unabhängig davon, ob das Promise
erfüllt oder abgelehnt wird.
Aufgabe 4: Erstellen Sie ein Array von Promises, die
alle nach unterschiedlichen Zeitspannen erfüllt werden. Verwenden Sie
Promise.all und die then-Methode, um eine
Meldung an die Konsole zu senden, wenn alle Promises erfüllt sind.
Aufgabe 5: Ändern Sie die Funktion aus Aufgabe 4 so,
dass eines der Promises abgelehnt wird. Verwenden Sie die
catch-Methode, um eine Fehlermeldung an die Konsole zu
senden, wenn mindestens eines der Promises abgelehnt wird.
Hier sind die Lösungen zu den Aufgaben:
Lösung Aufgabe 1:
function delayedPromise() {
return new Promise((resolve) => {
setTimeout(() => {
resolve('Promise wurde erfüllt!');
}, 2000);
});
}
delayedPromise().then((message) => {
console.log(message);
});Lösung Aufgabe 2:
function rejectedPromise() {
return new Promise((_, reject) => {
setTimeout(() => {
reject('Promise wurde abgelehnt!');
}, 3000);
});
}
rejectedPromise().catch((errorMessage) => {
console.error(errorMessage);
});Lösung Aufgabe 3:
function combinedPromise() {
return new Promise((resolve, reject) => {
setTimeout(() => {
Math.random() > 0.5 ? resolve('Promise wurde erfüllt!') : reject('Promise wurde abgelehnt!');
}, 2000);
});
}
combinedPromise()
.then((message) => {
console.log(message);
})
.catch((errorMessage) => {
console.error(errorMessage);
})
.finally(() => {
console.log('Promise-Operation abgeschlossen!');
});Lösung Aufgabe 4:
const promises = [
new Promise((resolve) => setTimeout(() => resolve('Promise 1 erfüllt!'), 1000)),
new Promise((resolve) => setTimeout(() => resolve('Promise 2 erfüllt!'), 2000)),
new Promise((resolve) => setTimeout(() => resolve('Promise 3 erfüllt!'), 3000)),
];
Promise.all(promises).then((messages) => {
console.log(messages);
});Lösung Aufgabe 5:
const promisesWithRejection = [
new Promise((resolve) => setTimeout(() => resolve('Promise 1 erfüllt!'), 1000)),
new Promise((_, reject) => setTimeout(() => reject('Promise 2 abgelehnt!'), 2000)),
new Promise((resolve) => setTimeout(() => resolve('Promise 3 erfüllt!'), 3000)),
];
Promise.all(promisesWithRejection)
.then((messages) => {
console.log(messages);
})
.catch((errorMessage) => {
console.error(errorMessage);
});Bitte beachten Sie, dass dies nur eine mögliche Lösung für jede Aufgabe ist. Es gibt viele Möglichkeiten, die Aufgaben zu lösen, insbesondere wenn man die Variationen in der Fehlerbehandlung und in der Verwaltung asynchroner Abläufe berücksichtigt.