Kolumne

Reactive Programming mit Angular – so geht’s!

Karsten Sitterberg

© Shutterstock / gdainti

In dieser Folge der Kolumne „Webentwicklung mit Angular“ zeigt Karsten Sitterberg, wie sich Reaktive Programmierung mit Angular umsetzen lässt. Zum Einsatz kommt dabei die Bibliothek RxJS.

Angular nutzt unter der Haube verschiedene Bibliotheken, um seine Funktionalitäten abzubilden. Eine dieser Bibliotheken ist RxJS 5. RxJS setzt Reactive-Extensions für JavaScript um. (Einige Hintergründe zu reaktiver Programmierung findet sich z.B. hier: https://www.slideshare.net/triondevelopment/reactive-everywhere).

Reactive Programming eignet sich besonders dafür, asynchrone Abläufe zu modellieren. Ein Beispiel für asynchrone Abläufe sind Zugriffe auf entfernte Quellen mittels HTTP. Angular bietet als HTTP-API den aus RxJS stammenden Observable-Typ. (Ein Promise-API steht dadurch ebenfalls bereit: Die Observables lassen sich mittels .toPromise() entsprechend umformen.)

Mit Angular CLI aufgesetzte Projekte deklarieren rxjs in Version ^5.0.0 als Abhängigkeit. Damit wird bei jedem npm install automatisch die aktuellste 5.x.x-Version von RxJS bezogen. Diese Version steht dann ab sofort im Projekt zur Verfügung.

Seit RxJS 5.4, das im Mai freigegeben wurde, ist der shareReplay-Operator verfügbar. Gerade im Angular-Umfeld bietet der Operator für den Zugriff auf HTTP-Resourcen einige interessante Eigenschaften:

  • Es werden Ergebnisse von erfolgreichen Verarbeitungen gepuffert.
  • Bei Fehlern wird das Resultat nicht gespeichert, eine Wiederholung ist somit möglich.
  • Es können mehrere Konsumenten bedient werden.

Unter der Haube ist der shareReplay-Operator eine Kombination der Effekte eines ReplaySubject, publishReplay() und refCount(). Er ist jedoch deutlich handlicher und leichter zu lesen, als wenn man die entsprechende Verarbeitungskette jedesmal erneut von Hand aufbauen würde.

Verwendet werden kann der Operator beispielsweise als eine Art Cache für die Antwort auf eine HTTP-Anfrage.

Im folgenden Code-Ausschnitt wird ein Service gezeigt, der HTTP-Anfragen auf zwei verschiedene Arten umsetzt: Einmal ohne shareReplay, einmal mit. Die Methode getGreeting() gibt ein einfaches Observable der HTTP-Anfrage zurück, während die Methode getGreetingShareReplay() ein mit shareReplay erweitertes Observable zurückliefert:

@Injectable()
export class ShareReplayService {
  private greeting = this.http.get(
    'http://rest-service.guides.spring.io/greeting?name=Angular'
  );
  private greetingShareReplay = this.greeting.shareReplay();
  constructor(private http: HttpClient) {
  }
  public getGreeting(): Observable<any> {
    return this.greeting;
  }
  public getGreetingShareReplay(): Observable<any> {
    return this.greetingShareReplay;
  }
}

 

Um zu verstehen, welche Auswirkungen der ShareReplay-Operator auf die HTTP-Anfrage hat, wird in einer Komponente der Einsatz beider Versionen der durch den Service bereitgestellten Observables gezeigt. Die Komponente ist im folgendem Code-Beispiel zu sehen.

@Component({
  selector: 'app-root',
  template: `
    
<h1> Welcome to ShareReplay!! </h1>


<h2> {{greeting | json }} </h2>


<h2> {{greetingShareReplay | json }} </h2>

    <button (click)="updateData()">Update</button>`
})
export class AppComponent {
  greeting: any;
  greetingShareReplay: any;
  constructor(private service: ShareReplayService) {
  }
  updateData() {
    this.service.getGreeting()
      .subscribe(greeting => this.greeting = greeting);
    this.service.getGreetingShareReplay()
      .subscribe(greeting => this.greetingShareReplay = greeting);
  }
}

 

Wichtig ist hierbei, dass in dieser Komponente mehrere Subscriptions auf ein und das selbe Observable von mehreren Komponenten aus simuliert wird. Dazu dient die Methode updateData(), welche an einen Button gebunden ist und somit bei jedem Button-Click eine neue Subscription auf beide Observables durchführt. Das Resultat des durch das Observable ausgeführten HTTP-Aufrufs wird dann einfach per JSON-Pipe im Template angezeigt, um beide Varianten dort direkt miteinander vergleichen zu können.

Die Auswirkungen werden auch in der folgenden Animation sichtbar: Das eine, “reguläre” Observable feuert bei jedem Subscribe einen neuen HTTP-Request ab, wodurch die Daten immer neu vom Server geholt werden. Dies ist erkennbar an der sich bei jedem Klick ändernden ID in der oberen Ausgabe.

Dagegen werden die Daten bei dem anderen, durch ShareReplay gepufferten Observable einmal per HTTP abgeholt und bei jedem weiteren Subscribe erneut zurückgegeben, ohne einen weiteren Request auszulösen. Dies ist in der unteren Ausgabe zu beobachten:

Der Umgang mit Fehlern durch ein mögliches retry() oder retryWhen() ergibt sich auf natürliche Weise: In dem Fall ändert sich das Verhalten gegenüber einem regulären Cold-Observable nicht.

Dieses kurze Beispiel zeigt, wie ausdrucksstark das Programmiermodell der Reactive-Extensions (Rx) ist. Wer aktuell noch ein Promise-basiertes API bevorzugt, sollte sich in jedem Fall mit den Möglichkeiten von Rx beschäftigen.

Geschrieben von
Karsten Sitterberg
Karsten Sitterberg
Karsten Sitterberg ist als freiberuflicher Entwickler, Trainer und Berater für Java und Webtechnologien tätig. Karsten ist Physiker (MSc) und Oracle-zertifizierter Java Developer. Seit 2012 arbeitet er mit trion zusammen.
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.