Reputation: 13
I am currently subscribing to an observable inside a while loop. The condition for while loop is a flag, whose value is updated based on some values from the current response. However the while loop doesn't wait for the flag to be updated (or code inside subscribe to be executed completely), and hence the loop runs infinitely.
The requirement is to continuously make new POST
request as long it return a response object which has a field "continue"
as "true"
and each request should be made using the updated values for varA
and varB
from current response.
For e.g. if after 4 subscribe and corresponding responses, if 5th response has {continue: "false"}
then the loop should terminate immediately making total iterations
So basically the loop should terminate immediately when res['continue']
is not "true"
.
However, in my case the loop is executing infinitely and the value for testLoop
is NOT becoming false
.
The loop is NOT updating the value for varA
and varB
in each iteration as well.
Basically the loop does not wait till subscribe code completes execution.
Method in component.ts file
let testLoop = true;
let varA = 100;
let varB = 0;
while(testLoop){
this.someService.sampleFunc(varA, varB).subscribe((res:any)=>{
//do something with res
if(res['continue'] == 'true'){
varA = res['A'];
varB = res['B'];
}
else{
testLoop = false;
}
})
}
Method in service.ts file
sampleFunc(varA: number, varB: number){
return this.http.post(url,{
varA: varA,
varB: varB
})
}
sample response from POST method
{
count: 100,
varA: 10,
varB: 10,
continue: "true"
}
I had gone through multiple threads suggesting different rxjs
operators, but could not find a solution which suits the above mentioned use case.
There was requirement which was missed. From each subscribe result, I also need to append to an array finalValues
and use that array once the loop finshes executing.
The code in component.ts would now look like:
let testLoop = true;
let varA = 100;
let varB = 0;
let finalValues = [];
while(testLoop){
this.someService.sampleFunc(varA, varB).subscribe((res:any)=>{
finalValues = finalValues.concat(res['values']);
if(res['continue'] == 'true'){
varA = res['A'];
varB = res['B'];
}
else{
testLoop = false;
}
})
}
sample response
{
values: ['A','B','C'],
count: 100,
varA: 10,
varB: 10,
continue: "true"
}
Upvotes: 1
Views: 777
Reputation: 57981
obs$ = this.sampleFunc(1,2)
sampleFunc(a:number,b:number)
{
return this.sampleCall(a,b).pipe(
tap((res) => {
..do something with "res"..
}),
switchMap((res:any) => {
if (res.continue == 'true') return this.sampleFunc(res.a, res.b);
return EMPTY;
})
);
}
sampleCall(a:number,b:number)
{
return this.http.post(url,{
varA: varA,
varB: varB
})
}
Then you simple subscribe to obs$, and don't forget unsubscribe!!
See stackblitz (in the stackblitz I use async pipe to subscribe/unsubscribe)
Update the instructions under tap are executed when the subscription is made, so in in res we have as response an object with a property "values" we can make some like
finalValues:any[]=[];
sampleFunc(a:number,b:number)
{
return this.sampleCall(a,b).pipe(
tap((res) => {
this.finalValues=this.finalValues.concat(res.values)
//or
this.finalValues=[...this.finalValues,...res.values]
}),
switchMap((res:any) => {
...
})
);
}
Upvotes: 0
Reputation: 14750
You can use the expand
operator to recursively call a method that returns an observable, then use takeWhile
to stop calling when a condition is met.
Something like this should work for you:
repeatSampleFunc$ = this.someService.sampleFunc(varA, varB).pipe(
expand(({A, B}) => this.someService.sampleFunc(A, B)),
takeWhile(({continue}) => continue === 'true', true)
);
repeatSampleFunc$.subscribe(
response => // do something with response
);
To accumulate the results into a single array, you can use the reduce
operator:
const EMPTY_RESPONSE = {
values: [],
count: 0,
varA: 0,
varB: 0,
continue: "true"
};
repeatSampleFunc$ = this.someService.sampleFunc(varA, varB).pipe(
expand(({A, B}) => this.someService.sampleFunc(A, B)),
takeWhile(({continue}) => continue === 'true', true),
reduce((finalValues, {values}) => finalValues.concat(values), this.EMPTY_RESPONSE)
);
Upvotes: 2
Reputation: 132
You can create a function to make the call and to handle the answer, if the condition to make a new request success it calls to itself again (recursively) if not, it ends.
keepAlive = () => {
this.service.makePostRequest().subscribe(answer=>{
if (!answer.condition) return
this.keepAlive()
})
}
Upvotes: 0