Promise 🤞
JavaScript ist Single-Threaded , was bedeutet, dass nicht zwei Skriptbits gleichzeitig ausgeführt werden können. Deshalb werden sie nacheinander ausgeführt. In Browsern teilt JavaScript einen Thread mit einer Menge anderer Dinge, was sich natürlich von Browser zu Browser unterschiedlich verhalten kann. In der Regel befindet sich JavaScript jedoch in derselben Queue (Warteschlange) wie das Aktualisieren von Styles und das Handling von Benutzeraktionen (z. B. Hervorheben von Text und Interaktion mit Formelementen). So kommt es unter Umständen zu einer langen Warteschlange.
Als Mensch ist man Multi-Threaded. Wir können mit mehreren Fingern tippen und gleichzeitig ein Gespräch führen. Beim Niesen allerdings werden alle aktuellen Aktivitäten für die Dauer des Niesens ausgesetzt. Das ist ziemlich ärgerlich und unter Umständen sogar gefährlich, besonders wenn man im Auto hinter dem Steuer sitzt und sich auf die Strasse konzentriert. Kein Code sollte also niesen.
Promises selbst sind nichts anderes als Objekte, die sozusagen als Platzhalter für das Ergebnis einer asynchronen Funktion dienen.
- Pending: der initiale State - der Prozess (GET/POST) ist noch nicht am Ende.
- Fulfilled: die Operation ist erfolgreich abgeschlossen und der Promise hat einen
resolved value
. Beispielsweise wird ein Promise mit einem JSON{}
erfolgreich alsvalue
vollendet. - Rejected: die Operation ist fehlgeschlagen. Dabei wird die Ursache des
Errors
angegeben.
Man spricht von einem settled
-Promise, wenn er sich nicht mehr im pending
befindet, d.h., dass er entweder fulfilled
oder rejected
wurde.
Geschirrspüler 🍽️
- Pending der Geschirrspüler läuft, hat aber den Spülprozess noch nicht beendet.
- Fulfilled der Geschirrspüler hat den Waschvorgang beendet und ist nun gefüllt mit sauberem Geschirr.
- Rejected der Geschirrspüler hat ein Problem. Die Kammer mit dem Tab wurde nicht verwendet. Das Geschirr ist noch dreckig.
Ist das Geschirrwaschen erfolgreich gewesen, kann man zum nächsten Schritt übergehen. Das Ausräumen kann nun beginnen. Im Falle Rejected kann man versuchen, den Waschvorgang zu wiederholen oder im worst case mit der Hand waschen.
Alle Promises ermöglichen dem Entwickler Logik für den fulfilled - als auch den rejected- Fall zu implementieren.
const geschirrspueler = document.querySelector('geschirrspueler');
geschirrspueler.addEventListener('load', () => {
// Waschvorgang erfolgreich beendet
}
)
geschirrspueler.addEventListener('error', () => {
// Geschirr ist immer noch dreckig
}
)
Achtung ⚠️: In diesem Beispiel, kann es unter Umständen beim laden von images dazu kommen, dass die Events vor dem listening passieren. Workarounds wären dann mit der property
complete als if
-Abfrage möglich:
function geladen() {
// Bild wurde geladen
}
if (image.complete) {
geladen();
} else {
image.addEventListener('load', geladen)
};
image.addEventListener('error', () => {
// Fehler
}
);
Versprochen ist Versprochen
Ein Promise
Objekt wird mithilfe vom Keyword new
und dem Promise constructor
erzeugt:
const executorFunction = (resolve, reject) => { };
const myFirstPromise = new Promise(executorFunction);
Der Promise
- Konstruktor bekommt einen function parameter
➡️ executor function. Dieser function parameter
wird ausgeführt, wenn der Konstruktor aufgerufen wurde. Der executor function startet i.d.R. eine asynchrone Operation und gibt vor wie der Promise
aufgefangen werden soll.
Der executor function besitzt zwei function parameter
, die normalerweise in resolve()
und reject()
aufgelöst sind. Diese zwei Parameter werden nicht vom Entwickler definiert.
Wird der Promise
- Konstruktor aufgerufen, übergibt Javascript seinen eigenen resolve()
und reject()
- Funktionen an die executor function:
resolve
nimmt nur ein Argument. Ein Blick unter die Haube zeigt, dass beim Aufrufen sich der Status desPromise
vonpending
(ausstehend) infulfilled
(erfüllt) auflöst. Dervalue
wird dann anresolve()
übergeben.reject
nimmt ebenfalls nur ein Argument, wobei dieses einreason
odererror
sein kann. Auch hier zeigt der Blick unter die Haube, dass beim Aufrufen sich der Status ändert und als Argument inreject()
übergeben wird.
Schauen wir uns ein Beispiel zum Promise
- Konstruktor an:
// false manuell setzen für reject
const meineBedingung = true;
const executorFunction = (resolve, reject) => {
if (meineBedingung) {
resolve('I resolved!');
} else {
reject('I rejected!');
}
}
const meineErsterPromise = new Promise(executorFunction);
- wir deklarieren eine Variable
meineErsterPromise
meineErsterPromise
- Konstruktor wird mitnew Promise()
aufgerufenexexutorFunction()
wird an den Konstruktor übergeben und hat zweifunction parameter
:resolve
undreject
- Wenn die Bedingung
meineBedingung
wahr/true
ist, dann wirdresolve()
aufgerufen ➡️ Ergebnis:'I resolved!'
- Wenn die Bedingung
meineBedingung
falsch/false
, dann wirdrejected()
aufgerufen ➡️ Ergebnis:'I rejected!'
Das ist ein ziemlich simples Beispiel. Einfach eine Bedingung, die True oder False zurückgibt und dann den Promise auflöst ist ziemlich banal. In der gängigen Praxis resultieren Promises
aus asynchronen Abfragen. Beispielsweise können Daten asynchron aus einer Datenbank abgefragt werden und das Ergebnis in einem Promise
je nach Ergebnis aufgelöst werden.
⚠️ Hier geht es nur um die Grundzüge von Promises
. Fortsetzung folgt…