Übersicht: JS Promise()

Ein Promise (deutsch: Versprechen) ist ein JavaScript-Objekt, das als Platzhalter für einen Wert dient, der noch nicht bekannt ist, aber irgendwann in der Zukunft verfügbar sein wird.

Sein Hauptzweck ist die saubere Verwaltung von asynchronen Operationen (wie das Laden von Daten aus dem Internet oder das Warten mit setTimeout), um die sogenannte „Callback Hell“ zu vermeiden.

1. Die drei Zustände (States)

Ein Promise befindet sich immer in einem von drei exklusiven Zuständen. Der Zustand kann sich nur von Pending zu Fulfilled oder Rejected ändern:

Zustand (State)BedeutungÜbergang
Pending (Ausstehend)Der Anfangszustand. Die asynchrone Operation läuft noch.Kann zu Fulfilled oder Rejected wechseln.
Fulfilled (Erfolgreich)Die Operation war erfolgreich. Der Wert ist verfügbar.Der finale Zustand. Das Promise ist aufgelöst (resolve).
Rejected (Fehlerhaft)Die Operation ist fehlgeschlagen. Ein Fehlergrund liegt vor.Der finale Zustand. Das Promise ist zurückgewiesen (reject).

Sobald ein Promise den Zustand Fulfilled oder Rejected erreicht, spricht man davon, dass es settled (abgeschlossen) ist. Es kann seinen Zustand danach nicht mehr ändern.

2. Erstellung eines Promise

a) Der new Promise()-Konstruktor

  • Das Schlüsselwort new erstellt ein neues Promise-Objekt.
  • Dieses Objekt startet sofort im Zustand Pending (Ausstehend).

b) Die Executor-Funktion

  • Der new Promise()-Konstruktor erwartet als einziges Argument eine Funktion. Diese wird als Executor-Funktion bezeichnet und wird sofort und synchron (sobald das Promise erstellt wird) ausgeführt.
  • Wichtig: Das ist der Ort, an dem du deine asynchrone Logik (z. B. setTimeout, fetch, Datenbankabfragen) platzierst.

c) Die magischen Argumente: resolve und reject

Die Executor-Funktion erhält automatisch zwei spezielle Callback-Funktionen von JavaScript als Argumente:

  • resolve (typischerweise der erste Parameter)
  • reject (typischerweise der zweite Parameter)

Die Funktion resolve(wert)

  • Zweck: Du rufst diese Funktion auf, wenn die asynchrone Operation erfolgreich abgeschlossen ist.
  • Zustandswechsel: Sie ändert den Zustand des Promises von Pending zu Fulfilled (Erfüllt/Erfolgreich).
  • Der Wert: Du übergibst den finalen Wert (z. B. geladene Daten, eine Erfolgsnachricht) als Argument an resolve(). Dieser Wert wird später im .then()-Handler des Konsumenten verfügbar sein.
  • Regel: Nach dem Aufruf von resolve() ignoriert das Promise alle weiteren Aufrufe von resolve() oder reject().

Die Funktion reject(grund)

  • Zweck: Du rufst diese Funktion auf, wenn während der asynchronen Operation ein Fehler aufgetreten ist.
  • Zustandswechsel: Sie ändert den Zustand des Promises von Pending zu Rejected (Abgelehnt/Fehlerhaft).
  • Der Grund: Du übergibst einen Fehlergrund (typischerweise ein Error-Objekt oder eine Fehlermeldung als String) als Argument an reject(). Dieser Grund wird später im .catch()-Handler des Konsumenten verfügbar sein.
  • Regel: Nach dem Aufruf von reject() ignoriert das Promise alle weiteren Aufrufe von resolve() oder reject().
// Konstante als Promise
const meinVersprechen = new Promise((resolve, reject) => {
  // Hier passiert die asynchrone Logik (z.B. setTimeout oder Datenabfrage)
  let success = true;

  if (succes) {
    resolve("Erfolgs-Wert"); // Übergibt den finalen Wert
  } else {
    reject("Fehlergrund"); // Übergibt den Fehlergrund
  }
});
// Funktion, die das Promise erzeugt und zurückgibt
function generiereZufallszahl() {
  return new Promise((resolve, reject) => {
    
    // 1. Die Logik (kann synchron oder asynchron sein)
    const zahl = Math.floor(Math.random() * 100) + 1; // Zahl zwischen 1 und 100
    
    if (zahl < 90) {
      // 2. Erfolg: Wenn die Bedingung erfüllt ist
      // Rufe resolve auf und übergebe die generierte Zahl
      resolve(zahl); 
      
    } else {
      // 3. Fehler: Wenn die Zahl zu hoch ist
      // Rufe reject auf und übergebe eine Fehlermeldung
      reject("Zahl zu hoch! Generierter Wert: " + zahl);
    }
  });
}
// Die Funktion 'warteKurz' als Arrow Function
const warteKurz = (ms) => {
  
  return new Promise((resolve, reject) => {
    
    const maxWartezeit = 3000; // 3 Sekunden Limit
    
    // 1. Synchroner Reject (wenn das Argument ungültig ist)
    if (ms > maxWartezeit) {
      
      // Das Promise wird sofort zurückgewiesen (Rejected)
      reject(new Error(`Die Wartezeit von ${ms} ms ist zu lang!`));
      return; 
    }
    
    // 2. Asynchrone Logik
    setTimeout(() => {
      
      // Das Promise wird nach der Wartezeit aufgelöst (Fulfilled)
      resolve(`Warten beendet nach ${ms} Millisekunden.`);
      
    }, ms);
    
  });
};

3. Verwendung eines Promise

Sobald ein Promise-Objekt erstellt wurde und sich im Zustand Pending befindet, hängen wir sogenannte Handler an, um die Benachrichtigung über das Ergebnis abzufangen, sobald das Promise settled (abgeschlossen) ist.

1. .then(onFulfilled) für den Erfolgsfall

  • Zweck: Fängt den Wert ab, der über resolve(wert) vom Executor übergeben wurde.
  • Wann ausgeführt: Der Handler wird aufgerufen, wenn das Promise in den Zustand Fulfilled wechselt.
  • Argument: Die Funktion in .then() erhält den Erfolgswert als Argument.

2. .catch(onRejected) für den Fehlerfall

  • Zweck: Fängt den Grund ab, der über reject(grund) vom Executor übergeben wurde.
  • Wann ausgeführt: Der Handler wird aufgerufen, wenn das Promise in den Zustand Rejected wechselt.
  • Argument: Die Funktion in .catch() erhält den Fehlergrund als Argument.
meinVersprechen
  .then((wert) => {
    // Wird ausgeführt, wenn resolve("Erfolgs-Wert") aufgerufen wird
    console.log("Erfolg:", wert);
  })
  .catch((fehler) => {
    // Wird ausgeführt, wenn reject("Fehlergrund") aufgerufen wird
    console.error("Fehler:", fehler);
  });
// Aufruf des Promise-Erzeugers
generiereZufallszahl()
  .then((zufallszahl) => {
    // ⬇ DIESER BLOCK wird ausgeführt, wenn resolve() im Executor aufgerufen wurde.
    // Der Parameter 'zufallszahl' entspricht dem Wert, der an resolve() übergeben wurde.
    console.log("Erfolg! Generierte Zahl:", zufallszahl);
  })
  .catch((fehlermeldung) => {
    // ⬇ DIESER BLOCK wird ausgeführt, wenn reject() im Executor aufgerufen wurde.
    // Der Parameter 'fehlermeldung' ist der String, der an reject() übergeben wurde.
    console.error("Fehler bei der Generierung:", fehlermeldung);
  });
// 1. Testfall: Erfolgreiche Wartezeit (resolve wird nach 1500ms ausgeführt)
warteKurz(1500)
  .then((nachricht) => {
    // Der resolve-Wert wird hier abgefangen
    console.log("✅ Wartezeit OK:", nachricht); 
  })
  .catch((fehler) => {
    // Dieser Block wird HIER NICHT ausgeführt
    console.error("❌ Fehler:", fehler.message);
  });

// 2. Testfall: Fehlerhafte Wartezeit (reject wird sofort ausgeführt)
warteKurz(5000)
  .then((nachricht) => {
    // Dieser Block wird HIER NICHT ausgeführt
    console.log("✅ Wartezeit OK:", nachricht); 
  })
  .catch((fehler) => {
    // Der reject-Grund (das Error-Objekt) wird hier abgefangen
    console.error("❌ Fehler:", fehler.message); 
    // z.B. Ausgabe: ❌ Fehler: Die Wartezeit von 5000 ms ist zu lang!
  });

4.Beispiele:

Funktion mit Promise return und timeout:

function simulateApiCall(success) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      success
        ? resolve("Daten erfolgreich abgerufen.")
        : reject("Fehler beim Abrufen der Daten.");
    }, 1000);
  });
}

simulateApiCall(true)
  .then((res) => console.log(res))  // => Daten erfolgreich agberufen.
    .catch((err) => console.log(err))
  

simulateApiCall(false)
  .then((res) => console.log(res))
  .catch((err) => console.log(err))  // => Fehler beim Abrufen der Daten.