Callbacks und Promises in JavaScript
Callbacks und Promises in JavaScript
~15 Min. Lesezeit Zuletzt aktualisiert am 8. August 2026
Mit dem Event-Loop-Wissen aus Kapitel 23 bauen wir jetzt die erste ECHTE asynchrone Operation unseres Haushaltsbuchs: Transaktionen aus einer Datei laden.
Das klassische Callback-Pattern
Vor Promises war der einzige Weg, auf ein asynchrones Ergebnis zu reagieren, eine Callback-Funktion zu übergeben, die AUFGERUFEN wird, sobald das Ergebnis vorliegt:
import { readFile } from 'node:fs';
readFile('transaktionen.json', 'utf-8', (fehler, inhalt) => {
if (fehler) {
console.error('Fehler beim Lesen:', fehler.message);
return;
}
console.log('Inhalt:', inhalt);
});
console.log('Diese Zeile läuft VOR dem Datei-Inhalt!'); // Call Stack aus Kapitel 23 in AktionCallback Hell: das Problem verschachtelter Callbacks
Sobald mehrere asynchrone Schritte NACHEINANDER ausgeführt werden müssen, führt das Callback-Pattern zu tief verschachteltem, schwer lesbarem Code:
ladeTransaktionen((fehler, transaktionen) => {
if (fehler) return console.error(fehler);
berechneSaldo(transaktionen, (fehler, saldo) => {
if (fehler) return console.error(fehler);
speichereSaldo(saldo, (fehler) => {
if (fehler) return console.error(fehler);
console.log('Fertig! Saldo gespeichert:', saldo);
// ... und so weiter, immer weiter nach rechts eingerückt
});
});
});Achtung: Dieses "Pfeilspitzen"-Muster ("Pyramid of Doom") ist der Hauptgrund, warum Promises eingeführt wurden – genau dieses Verschachtelungsproblem lösen sie.
Promises: ein Versprechen auf einen zukünftigen Wert
Ein Promise repräsentiert einen Wert, der JETZT noch nicht, aber IRGENDWANN verfügbar sein wird. Ein Promise befindet sich immer in genau einem von drei Zuständen: pending (ausstehend), fulfilled (erfüllt) oder rejected (fehlgeschlagen).
function ladeTransaktionenPromise() {
return new Promise((resolve, reject) => {
readFile('transaktionen.json', 'utf-8', (fehler, inhalt) => {
if (fehler) {
reject(fehler); // Promise wird 'rejected'
} else {
resolve(JSON.parse(inhalt)); // Promise wird 'fulfilled' mit diesem Wert
}
});
});
}
ladeTransaktionenPromise()
.then(transaktionen => console.log('Geladen:', transaktionen.length))
.catch(fehler => console.error('Fehler:', fehler.message));Promises verketten: das Ende der Callback Hell
.then() gibt selbst wieder ein Promise zurück – dadurch lassen sich mehrere asynchrone Schritte FLACH statt verschachtelt aneinanderreihen, das identische Beispiel von oben, jetzt lesbar:
ladeTransaktionenPromise()
.then(transaktionen => berechneSaldoPromise(transaktionen))
.then(saldo => speichereSaldoPromise(saldo))
.then(() => console.log('Fertig!'))
.catch(fehler => console.error('Irgendein Schritt ist fehlgeschlagen:', fehler.message));Mehrere Promises parallel: Promise.all
Sollen mehrere UNABHÄNGIGE asynchrone Operationen PARALLEL laufen (nicht nacheinander), ist Promise.all() das richtige Werkzeug:
Promise.all([
ladeTransaktionenPromise('januar.json'),
ladeTransaktionenPromise('februar.json'),
ladeTransaktionenPromise('maerz.json'),
])
.then(([januar, februar, maerz]) => {
console.log('Alle drei Monate geladen:', januar.length + februar.length + maerz.length);
})
.catch(fehler => console.error('Mindestens einer der drei ist fehlgeschlagen:', fehler.message));Achtung: Promise.all() schlägt SOFORT komplett fehl, sobald AUCH NUR EINES der übergebenen Promises fehlschlägt ("fail fast"). Sollen ALLE Ergebnisse eingesammelt werden, unabhängig davon, ob einzelne fehlschlagen, ist Promise.allSettled() die passendere Alternative.
Promises sind bereits deutlich lesbarer als verschachtelte Callbacks – in Kapitel 25 lernen wir mit async/await eine noch elegantere Syntax für GENAU dasselbe Konzept kennen.