Reputation: 31
Trying to find out can I await for all http calls in a recursive forkJoin. How can I await for the all the http calls in forkJoin and receive the final result?
Currently I have something like the following
public myMethod(urls: string[]) {
...
return forkJoin(observables)
.pipe(map(() => {
if (json.urls !== 0) {
return this.myMethod(json.urls);
}
}), catchError((err) => {
console.log(err);
return of(null);
})
);
}
And then I subscribe ...
public mainMehtod(){
return this.myMethod([json]).pipe(map(() => {
...some stuff here. But I need here the final result from the finished recursion
}));
EDIT
Well the solution I found was, collecting all the observables in a recursion and then calling the forkjoin.
Upvotes: 1
Views: 1648
Reputation: 1785
There's an expand
operator, that solves your problem.
Here's how you should use it:
import { fromEvent, of, forkJoin, EMPTY } from 'rxjs';
import { expand, delay, map, reduce, last } from 'rxjs/operators';
const DATABASE: Element[] = [
{id:'one', childId: 'three'},
{id:'two', childId: 'four'},
{id:'three', childId: 'five'},
{id:'four', childId: 'six'},
{id:'five', childId: null},
{id:'six', childId: null}
];
mainMethod(['one', 'two']).subscribe(value => {
console.log(value.map(e => e.id));
})
function mainMethod(ids: string[]) {
return forkJoin(ids.map(id => getElementById(id))).pipe(
expand(values => {
if (values.some(el => el.childId)) {
return forkJoin(values
.filter(v => v.childId)
.map(el => getElementById(el.childId))
)
}
return EMPTY;
}),
last() // use this if you want only last emission. Be careful, last throws an error, if no value is passed
// reduce((acc, values) => acc.concat(values), []) // use this if you want everything
)
}
function getElementById(id: string) {
return of(DATABASE.find(el => el.id === id)).pipe(
delay(1000)
)
}
interface Element {id: string, childId: string | null}
Working example https://stackblitz.com/edit/e499mk
Upvotes: 0
Reputation: 29325
expand
is a good way to go here. tough to make it precise with the provided code but it'd look like this:
private _myMethod(urls: string[]) {
...
return forkJoin(observables).pipe(
catchError(err => {
console.log(err)
return of(null)
})
)
}
public myMethod(urls: string[]) {
return this._myMethod(urls).pipe(
// expand will feed the value of outter observable to start, and then call this function recursively with the result of subsequent calls inside expand
expand(json => {
if (json.urls.length) {
// expand calls itself recursively
return this._myMethod(json.urls);
}
return EMPTY; // break
}),
// expand emits outter result and then results of recursion one by one. how you collect this is up to you
// reduce is an option to collect all values in an array like this (like a forkJoin of your forkJoins)
// reduce((acc, val) => acc.concat([val]), [])
// last is an option if you only want the last recursive call (or first if none from recursion)
// last()
// or omit these entirely if you want the results one by one.
)
}
Upvotes: 1