Skip to content
IRC-CodingIRC-Coding
Reactive ProgrammingObservableObserverRxJSDatenströmeAsynchronität

Einführung in Reactive Programming

Eine einfache Einführung in Reactive Programming: Warum reaktiv, Observer und Observable, Callbacks, Operatoren und praktische Beispiele.

S

schutzgeist

4 min read

Einführung in Reactive Programming

Stell dir eine typische Webanwendung vor, z. B. einen Online-Shop.

  • Ein Benutzer klickt auf einen Button (Produktdetails anzeigen).
  • Die Anwendung muss daraufhin eine Anfrage an einen langsamen Server schicken und auf die Antwort warten.
  • Erst wenn die Antwort da ist, kann sie die Seite aktualisieren.

1. Das Grundproblem: Warum überhaupt „Reaktiv”?

Das klassische (imperative) Modell: Die Anwendung blockiert den weiteren Ablauf, während sie auf die Antwort wartet. Das ist ineffizient! Es ist, als würdest du in einem Restaurant bestellen und dann den Kellner solange festhalten, bis dein Essen fertig ist. In der Zeit kann er keine anderen Gäste bedienen.

Die reaktive Lösung: Die Anwendung sagt dem Server: “Schick mir die Daten, wenn sie da sind. Ich warte nicht hier, aber ich hinterlasse dir meine Telefonnummer. Ruf mich zurück (call me back), wenn du was hast.” In der Zwischenzeit kann sie andere Aufgaben erledigen.

2. Das Kernprinzip: Asynchrone Datenströme

Reactive Programming dreht sich um zwei zentrale Ideen:

  1. Asynchronität: Aufgaben werden “hintergründig” ausgeführt. Das Hauptprogramm wird nicht blockiert und wartet nicht auf das Ergebnis.
  2. Datenströme (Streams): Alles kann als ein Strom (Sequence) von Ereignissen betrachtet werden: Mouse-Clicks, Tastatureingaben, HTTP-Anfragen, Datenbankergebnisse etc. Diese Ströme können erzeugt, transformiert, gefiltert und kombiniert werden.

3. Die Analogie: Das Abo-Modell (Zeitschrift vs. YouTube)

Um die reaktive Denkweise zu verstehen, vergleichen wir zwei Modelle:

Imperativ (Anfragenmodell)Reaktiv (Abo-Modell)
AnalogieEine Zeitschrift kaufenEinen YouTube-Kanal abonnieren
AktionDu gehst zum Kiosk und fragst aktiv nach der neuesten Ausgabe. (Pull)Du klickst auf “Abonnieren”. (Push)
ZukunftDu musst nächste Woche wieder zum Kiosk gehen, um die neue Ausgabe zu holen.Du bekommst eine Benachrichtigung, sobald ein neues Video online ist.
VorteilDu hast sofort, was du willst.Du verpasst nichts und musst nicht dauernd nachschauen. Der Inhalt kommt zu dir.

Reactive Programming funktioniert nach dem Abo-Modell (Push-Prinzip). Du abonnierst eine Datenquelle und wirst benachrichtigt, sobald neue Daten verfügbar sind.

4. Die wichtigsten Akteure: Observer und Observable

Diese Abstraktion wird in fast allen reactive Frameworks (wie RxJava, Project Reactor) verwendet.

  • Observable (oder Publisher):

    • Wer? Die Datenquelle. Sie produziert einen Strom von Ereignissen oder Daten.
    • Beispiele: Ein Button (produziert Click-Ereignisse), ein Sensor (produziert Messwerte), eine Server-Antwort.
    • Aufgabe: Sie kümmert sich darum, wer an ihren Daten interessiert ist (ihre “Abonnenten”).
  • Observer (oder Subscriber):

    • Wer? Der Datenkonsument. Er ist an den Daten des Observables interessiert.
    • Beispiele: Die Funktion, die auf einen Click reagiert; die Logik, die einen Sensorwert auswertet.
    • Aufgabe: Er abonniert ein Observable. Er muss drei verschiedene Arten von Benachrichtigungen verarbeiten können.

5. Die drei Callbacks: Wie der Observer reagiert

Der Observer muss dem Observable mitteilen, was bei welchem Event zu tun ist. Das macht er durch die Implementierung von drei Methoden:

  1. onNext(T value)

    • Wann? Wird aufgerufen, wenn das Observable ein neues Datenelement emittiert (aussendet).
    • Was tun? Verarbeite das empfangene Element (z. B. zeige die Produkte an, logge den Sensorwert).
    • YouTube-Analogie: Ein neues Video wurde hochgeladen.
  2. onError(Throwable error)

    • Wann? Wird aufgerufen, wenn ein Fehler aufgetreten ist.
    • Was tun? Fehlerbehandlung (z. B. zeige eine Fehlermeldung an dem Benutzer, versuche es erneut).
    • YouTube-Analogie: Beim Hochladen des Videos ist ein Fehler aufgetreten.
  3. onComplete()

    • Wann? Wird aufgerufen, wenn das Observable keine weiteren Daten mehr senden wird. Der Strom ist beendet.
    • Was tun? Aufräumarbeiten.
    • YouTube-Analogie: Der YouTuber hat seinen Kanal gelöscht. Es werden definitiv keine neuen Videos mehr kommen.

6. Ein Code-Beispiel (Konzeptionell)

Stell dir vor, wir wollen jeden Mouse-Click auf einer Webseite loggen.

Imperativer Ansatz (Pseudocode)

// Wir fragen aktiv ab (PULL)
while (true) {
  if (mouseWasClicked()) { // Blockiert, bis ein Click kommt!
    const clickEvent = getClickEvent(); // Holt den Click
    console.log('Click at: ', clickEvent.position);
  }
}
// Problem: Die ganze Schleife blockiert und wartet nur auf Clicks.

Reaktiver Ansatz (Pseudocode mit RxJS)

// Wir abonnieren die Click-Datenquelle (PUSH)
fromEvent(document, 'click') // Erzeugt ein Observable aus Click-Events
  .subscribe(                // Abonnieren und definieren der Callbacks
    (clickEvent) => {        // onNext: Was bei jedem Click passiert
      console.log('Click at: ', clickEvent.clientX, clickEvent.clientY);
    },
    (error) => {             // onError: Was bei einem Fehler passiert
      console.error('Something went wrong: ', error);
    },
    () => {                  // onComplete: Was am Ende passiert (wird hier nie aufgerufen, da Clicks nie aufhören)
      console.log('No more clicks. Goodbye!');
    }
  );
// Vorteil: Der Hauptthread ist nicht blockiert und kann andere Dinge tun.
// Die Callback-Funktion wird nur aufgerufen, wenn wirklich ein Event da ist.

7. Warum ist das so mächtig? Operatoren!

Die wahre Stärke liegt in der Möglichkeit, diese Datenströme mit Operatoren zu manipulieren, ähnlich wie map, filter, reduce in der funktionalen Programmierung.

  • filter: Nur bestimmte Events durchlassen. (Z. B.: nur Clicks innerhalb eines bestimmten Divs)
  • map: Events transformieren. (Z. B.: Ein Click-Event in ein Objekt mit {x: 10, y: 20} umwandeln)
  • debounceTime: Events “debouncen”. (Z. B.: Nur den letzten Click in einer schnellen Click-Sequenz verarbeiten – super für Suchfelder!)
  • merge: Mehrere Ströme zusammenführen. (Z. B.: Clicks und Tastatureingaben in einen gemeinsamen Strom)
fromEvent(document, 'click')
  .pipe(
    filter(event => event.target.id === 'myButton'), // Nur Clicks auf #myButton
    debounceTime(250),                               // Warte 250ms zwischen Clicks
    map(event => { return {x: event.clientX, y: event.clientY}; }) // Transformiere das Event
  )
  .subscribe(coord => console.log('Clicked at: ', coord));

Zusammenfassung

  • Reactive Programming ist ein Paradigma für die nicht-blockierende, asynchrone Verarbeitung von Datenströmen.
  • Es folgt dem Push- oder Abo-Modell: Der Datenkonsument (Observer) abonniert eine Datenquelle (Observable).
  • Der Observer reagiert auf drei Arten von Events: neue Daten (onNext), Fehler (onError), und Abschluss (onComplete).
  • Durch Operatoren (wie filter, map) können Datenströme elegant transformiert und kombiniert werden.
  • Vorteile: Bessere Ressourcennutzung (kein Blockieren), elegante Handhabung von asynchronen Events, leichtere Komposition komplexer Abläufe.
Zurück zum Blog
Share:

Nächster Artikel in Programmierung

Weiterlesen
Algorithmen und Datenstrukturen 2026

Ähnliche Beiträge