Reputation: 747
I'm struggling a bit with coming up with a good solution for doing multiple http requests, based on the result of each of them. So far, I've been doing good with switchMap, as long as it's only 2 http requests. However, I've found out I need to perform 3, and I'm not sure how to do it with switchMap, or if there's a better solution for it.
Basically, I will await a response, and if it comes back empty, I want to perform a 2nd request, and if that comes back empty, I want to perform a 3rd request.
Let me try to give a simplified example of what I've gotten so far:
getData(): Observable<number> {
const $subject = new Subject<number>();
// loadData() takes in an id to specify what data to load
service.loadData(1).pipe(
switchMap(response => {
if(response.length === 0) {
return service.loadData(2);
}
else {
return of(response);
}
}),
).subscribe(result => {
$subject.next(result);
$subject.complete();
});
return $subject.asObservable();
Now this works just fine, my issue is, that calling "loadData(2)", might also give back a response with length = 0, and in that case, I need to call loadData(3).
I tried having another switchMap within the switchMap, but that didn't seem to work properly. So my question is, how would I be able to check the length of the response of loadData(2), and if it's 0, return service.loadData(3) ?
Any help or tips are very welcome :)
Upvotes: 3
Views: 1446
Reputation: 6152
One possible solution is to use from
with pipe
and takeWhile -> concatMap -> filter -> tap
operators.
getData
in service will receive an array of id numbers to try getting data for.
from
will emit each id from the ids
array.
concatMap
does not subscribe to the next observable until the previous completes.
filter
will ignore empty results.
takeWhile
will cancel the whole stream once take
becomes false
getData(ids: number[]): Observable<number[]> {
let take = true;
return from(ids).pipe(
takeWhile(() => take),
concatMap(id => this.getDataFromApi(id)),
filter(result => result.length > 0),
tap(() => take = false)
);
}
Sample usage in the component:
handleGetData() {
console.log("component: getData");
const ids = [1, 2, 3, 4];
this.dataService.getData(ids).subscribe(data => {
console.log("got data: ", data);
});
}
Working stackblitz
Upvotes: 0
Reputation: 31125
One straight forward and ugly way would be to subscribe to each call individually and check it's result.
getData(): Observable<number> {
const $subject = new Subject<number>();
service.loadData(1).subscribe(
loadOneResponse => {
if(loadOneResponse.length === 0) {
service.loadData(2).subscribe(
loadTwoResponse => {
if(loadOneResponse.length === 0) {
service.loadData(3).subscribe(
loadThreeResponse => {
$subject.next(loadThreeResponse);
$subject.complete();
},
loadThreeError => {
}
);
} else {
$subject.next(loadTwoResponse);
$subject.complete();
}
},
loadTwoError => {
}
);
} else {
$subject.next(loadOneResponse);
$subject.complete();
}
},
loadOneError => {
}
);
return $subject.asObservable();
}
Upvotes: 2