Zaawansowane operatory w RxJS
Zaawansowane operatory w RxJS
W tym wpisie zostaną zaprezentowane zaawansowane operatory RxJS czyli operacje jakie możemy wykonywać na typie Observable czyli najważniejszym obiekcie w bibliotece RxJS reprezentującym strumień wartości w czasie do którego można się „zasubskrybować” otrzymując dane. Operatory jakie zostaną omówione to:
- mergeMap,
- concatMap,
- exhaustMap,
- switchMap
mergeMap
Zaczniemy od operatora mergeMap. Aby lepiej zobrazować jego działanie wyobraźmy sobie następująca sytuację – zamówienia w restauracji (z ang. orders) napływają od klientów do kucharza który je starannie przygotowuje. Kucharz przygotowuje dania równolegle. Kiedy jedno z tych dań jest skończone to kucharz je od razu wydaje. Ta prosta analogia opisuje działanie operatora mergeMap. Przejdźmy do kodu:
@Component({ selector: 'app-operators', template: `<button #button>Click Me</button>`, styleUrls: ['./operators.component.css'] }) export class OperatorsComponent implements OnInit { counter: number = 0; constructor() { } @ViewChild('button',{static:true}) button; clicks$:Observable<any>; orders_array = ["pizza", "ice scream", "pasta", "chicken"]; orderPreparation(order) { const delayTime = Math.floor(Math.floor(Math.random() * 10) * 1000) + 1; this.counter=this.counter+1 return of(`your order ${order} is ready after ${delayTime}`).pipe(delay(delayTime)); } ngOnInit(): void { this.clicks$ = fromEvent(this.button.nativeElement, 'click'); this.clicks$.pipe( mergeMap( order => { this.resetCounter(); return this.orderPreparation(this.orders_array[this.counter]);} ) ) .subscribe((item => console.log(item))); } resetCounter() { const MAX_ORDER = 4; if(this.counter >= MAX_ORDER) { this.counter = 0 } } }
W wyniku wykonania powyższego kodu otrzymamy po kliknięciu w button 4 razy:
your order ice scream is ready after 1001 your order chicken is ready after 3001 your order pizza is ready after 7001 your order pasta is ready after 8001
Kolejne wykonanie programu:
your order chicken is ready after 1001 your order ice scream is ready after 4001 your order pasta is ready after 5001 your order pizza is ready after 6001
Widzimy, że dania przygotowywane są równolegle a o kolejności ich wydania decyduje to które zostało już zakończone a NIE kolejność w jakiej trafiły one do kucharza.
concatMap
Drugi z operatorów concatMap pozwala respektować zamówienia klientów w takiej kolejności w jakiej one napływają od klientów. Jeżeli składane jest kolejne zamówienie to czeka ono do czasu aż poprzednie zostanie skończone. Dania są skolejkowane:
this.clicks$.pipe( concatMap( order => { this.resetCounter(); return this.orderPreparation(this.orders_array[this.counter]);} ) ) .subscribe((item => console.log(item))); }
W wyniku wykonania powyższego kodu otrzymamy po kliknięciu w button 4 razy:
your order pizza is ready after 2001 your order ice scream is ready after 1001 your order pasta is ready after 4001 your order chicken is ready after 6001
Kolejne wykonanie programu:
your order pizza is ready after 4001 your order ice scream is ready after 8001 your order pasta is ready after 8001 your order chicken is ready after 1001
Widzimy, że dania wydawane są w takiej kolejności w jakiej zostały one zamówione. Respektowana jest kolejność zamówień.
exhaustMap
Kolejny z operatorów exhaustMap:
this.clicks$.pipe( exhaustMap( order => { this.resetCounter(); return this.orderPreparation(this.orders_array[this.counter]);} ) ) .subscribe((item => console.log(item))); }
Dania niech będą przygotowane z opóźnieniem dokładnie 10 sekund:
const delayTime = Math.floor(Math.floor(1 * 10) * 1000);
Każde kolejne kliknięcie w button w trakcie przygotowywania zamówienia we wspomnianym czasie 10 sekund zostanie zignorowane. Kucharz ignoruje zatem inne zamówienia kiedy jest w trakcie przygotowywania dania. Możemy zlecić nowe zamówienie do kucharza, ale jego realizacji podejmie się on wtedy kiedy jest wolny i zakończył przygotowywać poprzednie danie – w przeciwnym wypadku zamówienie zostanie przez kucharza całkowicie zignorowane.
Każde kolejne kliknięcie w buton spowoduje całkowite zignorowanie poprzedniego kliknięcia (nowego zamówienia). Kliknięcie w button 4 razy w czasie 10 sekund spowoduje wyświetlenie w konsoli tylko:
your order pizza is ready after 10000
Kolejne kliknięcie w button 4 razy w czasie 10 sekund spowoduje wyświetlenie tylko:
your order ice scream is ready after 10000
switchMap
Ostatni z operatorów switchMap:
Kucharz otrzymując kolejne zamówienie porzuca poprzednie i zaczyna przygotowywać kolejne. W powyższym przykładzie dokonajmy modyfikacji na operator switchMap:
this.clicks$.pipe( switchMap( order => { this.resetCounter(); return this.orderPreparation(this.orders_array[this.count]);} ) ) .subscribe((item => console.log(item))); }
Każde kolejne kliknięcie w buton spowoduje zignorowanie poprzedniego kliknięcia (zamówienia) i respektowanie kolejnego. Kliknięcie w button 4 razy w czasie 10 sekund spowoduje wyświetlenie w konsoli:
your order chicken is ready after 10000
Leave a comment