1
0
Fork 0
mirror of https://github.com/TheTaz25/denis.ergin.git synced 2025-07-06 11:18:49 +00:00
denis.ergin/src/components/slides/javascript/07-async/promises.astro
2025-01-05 12:31:58 +01:00

315 lines
No EOL
11 KiB
Text

<section>
<section>
<h2>Promises</h2>
</section>
<section>
<p>Wie bereits zuvor erwähnt sind Promises ein <strong>Versprechen, dass etwas in er Zukunft fertig sein wird (Funktion)</strong>.</p>
<p>In heutzutage modernen Browsern ist jede lang-laufende Operation hinter einem solchen Promise versteckt.</p>
<p>Promises sorgen dafür das eine Funktion (Message) in die Queue gesteckt wird und das der Event-Loop diese später ausführt.</p>
</section>
<section>
<p>Ein Promise zu erstellen ist relativ simpel</p>
<pre class="js"><code data-trim data-line-numbers is:raw>
const myPromise = new Promise();
</code></pre>
</section>
<section>
<p>Dieses Promise macht nun aber nicht viel. Ein Promise benötigt eine Funktion die im Hintergrund ausgeführt wird.</p>
<pre class="js"><code data-trim data-line-numbers is:raw>
const myPromise = new Promise(() => {
console.log('Hallo Welt!');
});
</code></pre>
<p>Wir sollten beim ausführen dann ein "Hallo Welt!" in den Developer Tools sehen</p>
</section>
<section>
<p>Wie sieht das nun aber alles aus wenn wir schauen wollen in welcher Reihenfolge das alles ausgeführt wird?</p>
<pre class="js"><code data-trim data-line-numbers is:raw>
const myPromise = new Promise(() => {
console.log('Hallo Welt!');
});
console.log('Komme ich davor oder danach?');
</code></pre>
</section>
<section>
<p>Ein Promise macht auch nicht mehr als den Callback auszuführen und auf ein Resultat zu warten.</p>
<p>Wenn es nichts gibt auf das man warten muss, ist ein Promise auch sofort abgeschlossen.</p>
</section>
<section>
<p>Bevor wir weiter machen, gehen wir auf den Callback des Promises näher ein.</p>
<p>Dieser Callback erhält nämlich 2 Parameter, die wiederrum Callbacks sind</p>
<pre class="js"><code data-trim data-line-numbers is:raw>
const p = new Promise((resolve, reject) => {
// ???
});
</code></pre>
</section>
<section>
<p>Oft werden diese 2 Callbacks als <code>resolve</code> und <code>reject</code> definiert.</p>
<p>Schauen wir mal, was die Variable als Inhalt hat, in der wir dieses Promise packt:</p>
<pre class="js"><code data-trim data-line-numbers is:raw>
const myPromise = new Promise(() => {
console.log('Hallo Welt!');
});
// Promise { &lt;state&gt;: "pending" }
console.log(myPromise);
</code></pre>
</section>
<section>
<p>Das Promise hat einen Zustand der in "pending" hängt.</p>
<p>Dieser Zustand wird sich in dieser Form so auch nicht mehr ändern.</p>
<p>Hierfür sind die Callback-Funktionen <code>resolve &amp; reject</code> gedacht.</p>
</section>
<section>
<p>Rufen wir mal die Funktion <code>resolve</code> innerhalb des Promises auf</p>
<pre class="js"><code data-trim data-line-numbers is:raw>
const myPromise = new Promise((resolve) => {
console.log('Hallo Welt!');
resolve();
});
// Promise { &lt;state&gt;: "fulfilled", &lt;value&gt;: undefined }
console.log(myPromise);
</code></pre>
</section>
<section>
<p>Die Promise ist nun <strong>fulfilled</strong> und scheint einen <code>value</code> zu beinhalten, der in diesem Fall <code>undefined</code> ist.</p>
</section>
<section>
<p>Sehen wir uns noch kurz an, wenn der Promise <strong>rejected</strong> wird</p>
<pre class="js"><code data-trim data-line-numbers is:raw>
const myPromise = new Promise((_, reject) => {
console.log('Hallo Welt!');
reject();
});
// Promise { &lt;state&gt;: "rejected", &lt;value&gt;: undefined }
console.log(myPromise);
</code></pre>
</section>
<section>
<p>Gut, wir wissen nun dass der Promise "rejected" wurde, aber was heißt das genau?</p>
<p>Nun, Promises funktionieren ein wenig anders als einfach aufgerufene Funktionen.</p>
<p>Wenn wir die Variable <code>myPromise</code> untersuchen, sehen wir keine Möglichkeit, auf den Zustand oder den Wert zuzugreifen.</p>
<p>Aber es gibt ein paar Funktionen: <code>then, catch &amp; finally</code>.</p>
</section>
<section>
<p>Wir können nur auf Resultate und den Allgemeinen Ausgang eines Promise reagieren indem wir mit diesen Funktionen arbeiten.</p>
<p>Diese Funktionen sind <code>chainable</code>. Wir können mehrere <code>.then &amp; .catch</code> nacheinander aufrufen.</p>
<p>Wenn ein Promise erfolgreich ist und <code>resolve</code> aufruft, wird die Funktion in <code is:raw>.then(() => {})</code> aufgerufen</p>
</section>
<section>
<p>Sollte das Promise nicht erfolgreich gewesen sein und hat <code>reject()</code> aufgerufen, so wird <code is:raw>.catch(() => {})</code> aufgerufen.</p>
<p>Wir können nicht aus einem Promise "ausbrechen". Wir können aber Resultate aus einem Promise zu jederzeit in den äußeren Scope verschieben und weitere Funktionen aufrufen.</p>
</section>
<section>
<p>Sehen wir uns das ganze nun in Aktion an:</p>
<pre class="js"><code data-trim data-line-numbers is:raw>
const myPromise = new Promise((resolve, reject) => {
resolve();
});
myPromise.then(() => {
console.log('Das war erfolgreich!');
});
</code></pre>
</section>
<section>
<p>Der Fehlerfall ist auch leicht:</p>
<pre class="js"><code data-trim data-line-numbers is:raw>
const myPromise = new Promise((resolve, reject) => {
reject();
});
myPromise.catch(() => {
console.log('Das war diesmal NICHT erfolgreich!');
});
</code></pre>
</section>
<section>
<p>Führen wir beide Varianten einmal zusammen:</p>
<pre class="js"><code data-trim data-line-numbers is:raw>
const myPromise = new Promise((resolve, reject) => {
reject();
});
myPromise
.then(() => {
console.log('Das ging mal gut!');
})
.catch(() => {
// Durch das reject landen wir nur hier!
console.log('Das war diesmal NICHT erfolgreich!');
});
</code></pre>
</section>
<section>
<p>Das <code>catch</code> muss aber nicht das Ende sein, wenn wir nach dem <code>catch</code> noch etwas ausführen wollen, können wir einfach mit <code>.then</code> weiter machen.</p>
<pre class="js"><code data-trim data-line-numbers is:raw>
const myPromise = new Promise((resolve, reject) => {
reject();
});
myPromise
.then(() => {
console.log('Das ging mal gut!');
})
.catch(() => {
console.log('Das war diesmal NICHT erfolgreich!');
})
.then(() => {
console.log('Es gibt mehr danach!');
});
</code></pre>
</section>
<section>
<p>Das weitere <code>.then</code> und <code>.catch</code> aufgerufen werden können, hat einen entscheidenden Vorteil:</p>
<p>Wir können in <code>.then</code> und <code>.catch</code> einen Fehler werfen um ein anderes <code>.catch auszulösen.</code></p>
<p>Gleichzeitig bedeutet ein nicht vorhanden sein eines Errors, dass das nächste <code>.then</code> aufgerufen wird.</p>
</section>
<section>
<pre class="js"><code data-trim data-line-numbers is:raw>
const myPromise = new Promise((resolve, reject) => {
resolve();
});
myPromise
.then(() => {
console.log('Das ging mal gut!');
throw new Error('Pech gehabt!');
})
.catch(() => {
console.log('Das war diesmal NICHT erfolgreich!');
})
.then(() => {
console.log('Es gibt mehr danach!');
});
</code></pre>
</section>
<section>
<p>Um am Ende auf jeden Fall eine Funktion auszuführen gibt es zu guter letzt noch die Funktion <code>.finally</code>.</p>
<p>Der Aufruf sorgt dafür, dass in jedem Fall - egal ob ein Fehler geworfen wurde oder nicht - Code ausgeführt wird.</p>
</section>
<section>
<pre class="js"><code data-trim data-line-numbers is:raw>
const myPromise = new Promise((resolve, reject) => {
resolve();
});
myPromise
.then(() => {
console.log('Das ging mal gut!');
})
.catch(() => {
console.log('Das war diesmal NICHT erfolgreich!');
})
.finally(() => {
console.log('Ich geschehe immer!');
});
</code></pre>
</section>
<section>
<p>Parameter in <code>.catch</code> und <code>.then</code></p>
<p>Wir erhalten in diesen beiden Funktionen Informationen.</p>
<p><code>.catch</code> erhält in der Regel den Error der davor geworfen wurde, oder aber was in die Funktion <code>reject</code> mitgegeben wurde.</p>
</section>
<section>
<pre class="js"><code data-trim data-line-numbers is:raw>
const p = new Promise((_, reject) => {
reject('Zum Scheitern verdonnert');
});
p.catch((reason) => {
console.log(reason);
throw new Error('Für immer');
})
.catch((err) => {
console.log(err);
});
</code></pre>
</section>
<section>
<p>Das gleiche Funktioniert auch mit <code>resolve()</code> und <code>.then()</code></p>
<pre class="js"><code data-trim data-line-numbers is:raw>
const p = new Promise((resolve) => {
resolve(42);
});
p.then((zahl) => {
console.log(zahl);
return "Die einzig wahre Zahl";
})
.then((x) => {
console.log(x);
});
</code></pre>
</section>
<section>
<p>Ein Konkretes Beispiel</p>
<p>Nachfolgend lesen wir den Inhalt aus dem System-Clipboard aus. Die Funktionalität, die uns der Browser dafür gibt, liefert einen Promise.</p>
</section>
<section>
<pre class="js"><code data-trim data-line-numbers is:raw>
const into = document.getElementById('clipboard-result');
document.getElementById('clipboard-paste').onclick = function () {
navigator.clipboard.readText()
.then((clipboardText) => {
into.innerText = `Kopierter Text:\n${clipboardText}`;
})
.catch((err) => {
into.innerText = `Fehler beim lesen des Clipboards: ${err}`;
});
}
</code></pre>
<button id="clipboard-paste">
Paste
</button>
<div id="clipboard-result">
Ergebnis...
</div>
<script>
const into = document.getElementById('clipboard-result')!;
document.getElementById('clipboard-paste')!.onclick = function () {
navigator.clipboard.readText()
.then((clipboardText) => {
into.innerText = `Kopierter Text:\n${clipboardText}`;
})
.catch((err) => {
into.innerText = `Fehler beim lesen des Clipboards: ${err}`;
});
}
</script>
</section>
</section>