I have a fairly complicated reactive behavior which I would like to achieve using RxJS but I didn't find a proper solution yet.
In my angular app which can be found in Plnkr I have a series of sync, async and parallel async execution.
First the user clicks on a button which calls next on my RxJS Subject
userClick$ = new Subject<void>();.<button (click)="model.on = !model.on; userClick$.next()"> {{ model.on ? 'Stop' : 'Start' }} </button>- then I provide a random number using
getRandomNumber - then I calculate the exponential of that random number using
getExpNumber - Finally I need to run
getFloorandgetCeilall in parallel.
What I need?
- Be able to use
forkJoinon the last two parallel executions #4. - Be able to express dependency of #3 on #2 and #4 on #3:
- getExpNumber depends on getRandomNumber and run after it
- getFloor runs in parallel with getCeil and both depend on getExpNumber.
- Be able to use the return value of the stream in each of my
flatMap, currently I get only the last value (I need to display the random value (first flatMap) to the user usingngForandasyncpipe.
Depencency: I can acheive that by checking the type of my current observable value in each flatMap but I am thinking there must be a better way.
Full code:
import { Component, NgModule, VERSION } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';
import 'rxjs/add/operator/filter';
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/switchMap';
@Component({
selector: 'my-app',
template: `
<button (click)="model.on = !model.on; userClick$.next()">{{ model.on ? 'Stop' : 'Start' }}</button>
<h1>Numbers stream</h1>
<div *ngFor="let numberValue of (numbers$ | async)">
<h2>{{ numberValue }}</h2>
</div>
<label>{{ model.log }}</label>
`,
})
export class App {
model = {
on: false,
log: ''
}
userClick$ = new Subject<void>();
numbers$: Observable<number[]> = this.userClick$
.filter(() => !!this.model.on)
.do(() => this.model.log = '')
.switchMap(() => this.getRandomNumber())
.switchMap((num) => this.getExpNumber(num))
.switchMap((num) => this.getFloor(num))
.switchMap((num) => this.getCeil(num));
constructor() {
}
/**
* Runs after user click
*/
getRandomNumber(): Observable<number[]> {
return new Observable<number[]>(observer => {
this.model.log += ' getRandomNumber';
observer.next([Math.floor(Math.random() * 10) + 1]);
return () => {}
}
}
/**
* Depends on getRandomNumber and run after it
*/
getExpNumber(n: number): Observable<number[]> {
return new Observable<number[]>(observer => {
this.model.log += ' getExpNumber';
observer.next([Math.floor(Math.exp(n)]);
return () => {}
}
}
/**
* Runs in parallel with getCeil
*/
getFloor(n: number): Observable<number[]> {
return new Observable<number[]>(observer => {
this.model.log += ' getFloor';
observer.next([Math.floor(n)]);
return () => {}
}
}
/**
* Runs in parallel with getFloor
*/
getCeil(n: number): Observable<number[]> {
return new Observable<number[]>(observer => {
this.model.log += ' getCeil';
observer.next([Math.ceil(n)]);
return () => {}
}
}
}
@NgModule({
imports: [ BrowserModule ],
declarations: [ App ],
bootstrap: [ App ]
})
export class AppModule {}