Skip to content

Kapitel 11: Promise (Asynchrone Programmierung)

11.1 Was ist Callback Hell?

❌ Das Callback-Hell Problem

Callback Hell (auch Pyramid of Doom genannt) entsteht, wenn mehrere asynchrone Operationen verschachtelt sind.

javascript
// ❌ Callback Hell (Pyramid of Doom)
getUser(userId, function(user) {
  getPosts(user.id, function(posts) {
    getComments(posts[0].id, function(comments) {
      getAuthor(comments[0].authorId, function(author) {
        console.log(author);
        // ... noch tiefer? 😱
      }, function(error) {
        console.error(error);
      });
    }, function(error) {
      console.error(error);
    });
  }, function(error) {
    console.error(error);
  });
}, function(error) {
  console.error(error);
});

Probleme:

  • 🚫 Schwer zu lesen
  • 🚫 Schwer zu warten
  • 🚫 Fehlerbehandlung ist chaotisch
  • 🚫 "Callback-Hell"

11.2 Promise Grundlagen

📦 Was ist ein Promise?

Ein Promise ist ein Objekt, das einen asynchronen Vorgang repräsentiert. Es kann drei Zustände haben:

ZustandBeschreibung
Pending (Anstehend)Vorgang läuft noch
Fulfilled (Erfüllt)Vorgang erfolgreich abgeschlossen
Rejected (Abgelehnt)Vorgang fehlgeschlagen
javascript
// Syntax
const promise = new Promise((resolve, reject) => {
  // Asynchrone Operation
  if (/* Erfolg */) {
    resolve(ergebnis);   // In Fulfilled-Zustand
  } else {
    reject(fehler);     // In Rejected-Zustand
  }
});

🎯 Einfaches Beispiel

javascript
// Promise erstellen
const myPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    const success = true;
    if (success) {
      resolve("Daten erfolgreich geladen! ✅");
    } else {
      reject("Fehler beim Laden! ❌");
    }
  }, 1000);
});

// Promise verwenden
myPromise
  .then(result => console.log(result))
  .catch(error => console.error(error));

Ausgabe (nach 1 Sekunde):

Daten erfolgreich geladen! ✅

11.3 then() catch() finally()

then() - Bei Erfolg

javascript
const promise = new Promise((resolve, reject) => {
  resolve("Erfolg!");
});

promise.then(result => {
  console.log(result); // "Erfolg!"
});

Verkettung (Chaining):

javascript
promise
  .then(result => result + " 🎉")
  .then(result => console.log(result))  // "Erfolg! 🎉"
  .then(() => "Nächster Schritt")
  .then(result => console.log(result)); // "Nächster Schritt"

catch() - Bei Fehler

javascript
const promise = new Promise((resolve, reject) => {
  reject("Fehler!");
});

promise
  .then(result => console.log(result))
  .catch(error => console.error(error));  // "Fehler!"

Fehlerbehandlung in der Kette:

javascript
promise
  .then(result => {
    throw new Error("Ein Fehler!");
  })
  .then(result => console.log("Wird nicht ausgeführt"))
  .catch(error => console.error(error));  // "Ein Fehler!"

🔄 finally() - Immer ausführen

javascript
const promise = new Promise((resolve, reject) => {
  resolve("Erfolg!");
});

promise
  .then(result => console.log(result))
  .catch(error => console.error(error))
  .finally(() => {
    console.log("Bereinigung (immer ausgeführt)");
  });

Praxisbeispiel:

javascript
function fetchData(url) {
  console.log("Laden...");  // Loading...
  
  return fetch(url)
    .then(response => response.json())
    .catch(error => console.error(error))
    .finally(() => {
      console.log("Laden beendet");  // Loading finished
    });
}

11.4 Promise.all() - Parallele Anfragen

📦 Was macht Promise.all()?

Promise.all() führt mehrere Promises parallel aus und gibt die Ergebnisse in einem Array zurück.

javascript
// Syntax
Promise.all([promise1, promise2, ...])
  .then(results => { /* Alle erfolgreich */ })
  .catch(error => { /* Mindestens eines fehlgeschlagen */ });

🎯 Beispiel: Mehrere API-Aufrufe parallel

javascript
const fetchUser = fetch("https://api.example.com/user/1").then(r => r.json());
const fetchPosts = fetch("https://api.example.com/user/1/posts").then(r => r.json());
const fetchComments = fetch("https://api.example.com/user/1/comments").then(r => r.json());

Promise.all([fetchUser, fetchPosts, fetchComments])
  .then(([user, posts, comments]) => {
    console.log("User:", user);
    console.log("Posts:", posts);
    console.log("Comments:", comments);
  })
  .catch(error => console.error("Ein Fehler ist aufgetreten:", error));

Vorteil: Parallele Ausführung (schneller!)


⚠️ Verhalten bei Fehlern

javascript
const p1 = Promise.resolve(1);
const p2 = Promise.reject("Fehler");
const p3 = Promise.resolve(3);

Promise.all([p1, p2, p3])
  .then(results => console.log(results))
  .catch(error => console.error(error));  // "Fehler" ❌

Wichtig: Wenn ein Promise fehlschlägt, wird sofort der catch()-Block ausgeführt.


11.5 Promise.race() - Das schnellste gewinnt

🏎️ Was macht Promise.race()?

Promise.race() gibt das Ergebnis des ersten Promises, das abgeschlossen wird (egal ob Erfolg oder Fehler).

javascript
// Syntax
Promise.race([promise1, promise2, ...])
  .then(result => { /* Das schnellste gewinnt */ })
  .catch(error => { /* Das schnellste schlägt fehl */ });

🎯 Beispiel: Timeout-Simulation

javascript
const fetchData = new Promise((resolve, reject) => {
  setTimeout(() => resolve("Daten geladen"), 2000);
});

const timeout = new Promise((resolve, reject) => {
  setTimeout(() => reject("Timeout!"), 1000);
});

Promise.race([fetchData, timeout])
  .then(result => console.log(result))
  .catch(error => console.error(error));  // "Timeout!" ⏰

11.6 Praxis: API-Anfragen simulieren

🌐 Beispiel 1: Einfache API-Simulation

javascript
// API-Aufruf simulieren
function fetchUser(id) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (id > 0) {
        resolve({ id, name: "Max", email: "max@example.com" });
      } else {
        reject("Ungültige ID");
      }
    }, 1000);
  });
}

// Verwenden
fetchUser(1)
  .then(user => console.log("User:", user))
  .catch(error => console.error("Fehler:", error));

🌐 Beispiel 2: Verkettete API-Aufrufe

javascript
function fetchUser(id) {
  return new Promise(resolve => {
    setTimeout(() => resolve({ id, name: "Max" }), 500);
  });
}

function fetchPosts(userId) {
  return new Promise(resolve => {
    setTimeout(() => resolve([{ title: "Post 1" }, { title: "Post 2" }]), 500);
  });
}

// Verkettung
fetchUser(1)
  .then(user => {
    console.log("User:", user);
    return fetchPosts(user.id);
  })
  .then(posts => {
    console.log("Posts:", posts);
  })
  .catch(error => console.error(error));

🌐 Beispiel 3: Parallele API-Aufrufe

javascript
function fetchUser() {
  return new Promise(resolve => {
    setTimeout(() => resolve({ name: "Max" }), 1000);
  });
}

function fetchPosts() {
  return new Promise(resolve => {
    setTimeout(() => resolve([{ title: "Post 1" }]), 800);
  });
}

function fetchComments() {
  return new Promise(resolve => {
    setTimeout(() => resolve([{ text: "Kommentar 1" }]), 1200);
  });
}

// Alle parallel ausführen
Promise.all([fetchUser(), fetchPosts(), fetchComments()])
  .then(([user, posts, comments]) => {
    console.log("User:", user);
    console.log("Posts:", posts);
    console.log("Comments:", comments);
  })
  .catch(error => console.error(error));

11.7 Asynchroner Ablauf - Elegante Schreibweise

🎯 Callback Hell vs Promise

javascript
// ❌ Callback Hell
function getUser(callback) {
  setTimeout(() => {
    callback({ name: "Max" });
  }, 500);
}

function getPosts(user, callback) {
  setTimeout(() => {
    callback([{ title: "Post 1" }]);
  }, 500);
}

getUser((user) => {
  getPosts(user, (posts) => {
    console.log(user, posts);
  });
});

// ✅ Promise (eleganter)
function getUser() {
  return new Promise(resolve => {
    setTimeout(() => resolve({ name: "Max" }), 500);
  });
}

function getPosts(user) {
  return new Promise(resolve => {
    setTimeout(() => resolve([{ title: "Post 1" }]), 500);
  });
}

getUser()
  .then(user => {
    console.log(user);
    return getPosts(user);
  })
  .then(posts => console.log(posts))
  .catch(error => console.error(error));

Vorteile von Promise:

  • ✅ Bessere Lesbarkeit
  • ✅ Einfachere Fehlerbehandlung
  • ✅ Verkettung (Chaining) möglich
  • ✅ Parallele Ausführung mit Promise.all()

🎉 Zusammenfassung

In diesem Kapitel hast du gelernt:

  • ✅ Was Callback Hell ist und warum es schlecht ist
  • ✅ Promise Grundlagen (Pending, Fulfilled, Rejected)
  • then(), catch(), finally()
  • Promise.all() für parallele Anfragen
  • Promise.race() für Wettbewerb-Szenarien
  • ✅ Praxisanwendungen (API-Anfragen simulieren)

📝 Übung

  1. Promise erstellen:

    javascript
    // Erstelle ein Promise, das nach 1 Sekunde "Hallo" zurückgibt
  2. Verkettung:

    javascript
    // Verketet zwei Promises: 
    // 1. Zahl verdoppeln
    // 2. Ergebnis verdreifachen
  3. Promise.all():

    javascript
    // Erstelle 3 Promises und führe sie parallel aus

➡️ Nächstes Kapitel

In Kapitel 12 lernen wir async/await - die modernste und eleganteste Art, asynchronen Code zu schreiben!

Frei für alle Anfänger