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

Zobacz kod na GitHubie i zapisz się na bezpłatny newsletter!

.

Leave a comment

Your email address will not be published.


*