Angular - typ Observable

Stronę tą wyświetlono już: 46 razy

Wcześniej już poznany typ Promise umożliwiał wykonywanie asynchroniczne kodu a także wywołanie funkcji na określone przypadki, jakie mogły zajść w trakcie wykonywania takiego kodu. Tym razem omówię użycie typu Observable, który to umożliwia obserwowanie obiektu poprzez subskrypcję realizowaną w sposób asynchroniczny (na strumieniach) lub synchroniczny (na zmiennych). Typ Observable umożliwia łatwiejsze operowanie na zwracanych wartościach przy pomocy sporej liczby dostępnych operatorów RxJs. Umożliwiają one łączenie kilku strumieni i oczekiwanie na wykonanie ich za jednym razem (operator forkJoin, wykonywanie operacji dodatkowych przed subskrypcją (operator tap, przełączanie się z jednego obiektu Observable na drugi (operator switchMap. Oraz wiele, wiele innych operacji. Observable wysyła informacje tylko do tych, którzy się zasubskrybowali.

Coby już nie przynudzać oto prosty przykład wywołania subskrypcji na zbiorze obiektów:

Listing 1
  1. const obs = of(0, 1, 2); // operator RxJs, który z zwykłych zmiennych tworzy synchroniczny obiekt Observable
  2. obs.subscribe( {
  3. next: (value: number) => { // jak się wykona
  4. console.log('Value: ', value);
  5. },
  6. error: (error) => { // jak będzie błąd
  7. // some error stuff
  8. },
  9. complete: () => { // jak się zakończy (powodzeniem lub nie)
  10. // it's done
  11. }
  12. });

Wynik działania funkcji:

Value: 0
Value: 1
Value: 2

Samodzielne utworzenie obserwatora wygląda następująco:

Listing 2
  1. const observable = new Observable((observer) => {
  2. observer.next('Some data to receive'); // and here we go, the next function that return some data to subscriber!
  3. observer.complete(); // and here is some kind of complete method call
  4. });
  5. const observer = observable.subscribe( // w tym momencie uzyskiwany jest obiekt subskrybenta, który posiada metodę unsubscribe
  6. (value) => {
  7. console.log(value);
  8. },
  9. (error) => {
  10. // errors here
  11. },
  12. () => {
  13. // complete
  14. }
  15. );
  16. observer.unsubscribe(); // tutaj subskrybent się odsubskrybowuje (chociaż w tym przypadku nie jest to potrzebne)

Jak widać powyżej zastosowałem nieco uproszczoną wersję subskrypcji. Wynik działania powyższego kodu będzie następujący:

Some data to receive

Oczywiście w przypadku, gdy gdy obiekt typu Obervable kończy się po jednokrotnym wywołaniu to nie ma sensu odsubskrybowanie się. Taki sens istnieje, gdy obiekt obserwatora wykonuje nieskończoną liczbę wywołań. Wtedy w destruktorze ngOnDestroy konieczne będzie odsubskrybowanie się. Jeżeli tego nie zrobisz będziesz miał wycieki pamięci. Oto przykład takiego wywołującego się w nieskończoność obserwatora:

Listing 3
  1. observer: Subscription; // obiekt typu Subscription będący subskrybentem tworzonego obiektu obserwatora
  2. observable: Observable<any>; // nasz ukochany obserwator
  3. ngOnInit() {
  4. this.observable = new Observable((observer) => {
  5. setInterval(() => {
  6. observer.next(Math.random());
  7. }, 500);
  8. });
  9. this.observer = this.observable.subscribe(
  10. (value) => {
  11. console.log(value);
  12. },
  13. (error) => {
  14. // errors here
  15. },
  16. () => {
  17. // complete
  18. }
  19. );
  20. }
  21. ngOnDestroy() {
  22. this.observer.unsubscribe();
  23. }

Teraz program subskrybent będzie otrzymywał informacje dopóty, dopóki się nie odsubskrybuje co jest realizowane w destruktorze ngOnDestroy. Jeżeli tego nie zrobisz to będziesz mieć wycieki pamięci ponieważ przy każdych "odwiedzinach" komponentu tworzyłby się nowy wątek, nowy subskrybent i takich wątków mogło by się po czasie natworzyć całkiem sporo.

Wynik działania powyższego obserwatora będzie taki, że co 500ms będzie wysyłał do subskrybenta info z wylosowaną wartością liczbową. Jak ci się znudzi to wystarczy się odsubskrybować co przerwie nieskończoną pętlę.

0.11802575720224517
0.47792331496191487
0.17220013882498897
0.17311422383397634
0.5920673554985459
0.053330048792298745
0.5111122552877853
0.5808523271907068
0.8702832698892748
0.6436521608916241
... (on to the end of component life)

Komentarze