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 }}