You may consider using the below approach See Code on Stackblitz
id = 1;
Values = { info: true };
get data() { return { id: this.id,info: this.Values.info}}
showProgressSubject$ = new BehaviorSubject(true);
showProgressAction$ = this.showProgressSubject$.asObservable();
currentStatusSubject$ = new Subject<string>();
currentStatus$ = this.currentStatusSubject$.asObservable()
stoppedSubject$ = new Subject();
stopped$ = this.stoppedSubject$.asObservable();
startedSubject$ = new Subject();
started$ = this.startedSubject$.asObservable();
interval = 500; // Change to 3000 for 3s
maxTrialTime = 6000;// Change to 120000 for 2min
timer$ = timer(0, this.interval).pipe(
tap((i) => {
if(this.maxTrialTime/this.interval < i) { this.stoppedSubject$.next()}
}),
takeUntil(this.stopped$),
repeatWhen(() => this.started$)
)
apiOneCall$ = this.userServ.start(this.data);
apiTwoCall$ = this.apiOneCall$.pipe(
switchMap(({Id}) => Id ? this.Service.getStatus(Id): throwError('No Id')),
tap((res) => this.currentStatusSubject$.next(res)),
tap(res => console.log({res})),
tap((res) => {if(res === 'created') {this.stoppedSubject$.next()}})
)
trialCallsToApiTwo$ = this.timer$.pipe(mergeMap(() => this.apiTwoCall$))
In your Html you can use the async pipe
Show Progress : {{ showProgressAction$ | async }} <br>
Timer: {{ timer$ | async }}<br>
Response: {{ trialCallsToApiTwo$ | async }}<br>
<button (click)="startedSubject$.next()">Start</button><br>
<button (click)="stoppedSubject$.next()">Stop</button><br>
Explanation
We begin by setting up the properties id
, Values
and data
being a combination of the 2 values
id = 1;
Values = { info: true };
get data() { return { id: this.id,info: this.Values.info}}
We then create a Subject
to help with tracking of the progress of the operations. I am using BehaviorSubject to set the initial value of showing Progress to true.
We will use currentStatus$
to store whether current state is 'in_progress' or 'created'
stopped$
and started
will control our observable stream.
You may have a look at the below post What is the difference between Subject and BehaviorSubject?
showProgressSubject$ = new BehaviorSubject(true);
showProgressAction$ = this.showProgressSubject$.asObservable();
currentStatus$ = this.currentStatusSubject$.asObservable()
stoppedSubject$ = new Subject();
stopped$ = this.stoppedSubject$.asObservable();
startedSubject$ = new Subject();
started$ = this.startedSubject$.asObservable();
Next we define interval = 500; // Change to 3000 for 3s
and maxTrialTime = 6000;// Change to 120000 for 2min
We then define a timer$
observable using the timer
operator. The operator is used to generate a stream of values at regular interval
We set the delay to 0
and the interval to interval
property we had earlier created
We then tap
into the observable stream. The tap
operator allows us perform an operation without changing the observable stream
In our tap operator, we check whether the maximum time has been reached and if it has we call the next function on stoppedSubject$
. We pipe our stream to takeUntil(this.stopped$)
to stop the stream and repeatWhen(() => this.started$)
to restart the stream
timer$ = timer(0, this.interval).pipe(
tap((i) => {
if(this.maxTrialTime/this.interval < i) { this.stoppedSubject$.next()}
}),
takeUntil(this.stopped$),
repeatWhen(() => this.started$)
)
The Remaining part is to make a call to the apis
We will use switchMap
to combine the two observables. switchMap
will cancel any earlier request if a new request is made. If this is not your desired behaviour you may consider exhaustMap
or the mergeMap
operators
From the result of apiOneCall$
if no id, we use the throwError
operator to indicate an error otherwise we return a call to apiTwo
We tap into the result of apiTwoCall$
and call the next function on currentStatusSubject$
passing in the response. This sets the value of currentStatus$
to the result of the response
The line tap((res) => {if(res === 'created') {this.stoppedSubject$.next()}})
taps into the result of apiTwoCall$
and if it is 'created' it stops the timer
apiOneCall$ = this.userServ.start(this.data);
apiTwoCall$ = this.apiOneCall$.pipe(
switchMap(({Id}) => Id ? this.Service.getStatus(Id): throwError('No Id')),
tap((res) => this.currentStatusSubject$.next(res)),
tap(res => console.log({res})),
tap((res) => {if(res === 'created') {this.stoppedSubject$.next()}})
)
Now we finally combine the timer$
and apiTwoCall$
with mergeMap
operator trialCallsToApiTwo$ = this.timer$.pipe(mergeMap(() => this.apiTwoCall$))
In Our HTML we can then use the async
pipe to avoid worrying about unsubscribing
{{ trialCallsToApiTwo$ | async }}