RxJS Observable returning array, run another function with each array iteration
Problem
- /list --> Item[]
- /item?item_id={id} --> Item with extra stuff
- let's get Item[] with extra stuff
Solution concatMap to zip itemRequests
getList(): Observable<Item> {
return this.http.get('/list').pipe(
concatMap(list => {
const itemRequests = list.map(item => this.getItem(item))
return zip(...itemRequests)
})
)
}
getItem(item: Item): Observable<Item> {
return this.http.get(`/item?item_id=${item.id}`)
}
Done!
getList().subscribe((items: Item[]) => {
console.log(items)
})
You probably want to use the concatMap operator instead of map. concatMap flattens the returning Observable to the source Observable, while map returns an observable of observable.
If you want data from getItem() to be in the same order listed in 'all_news_url', you could try something like
this.http.get('all_news_url')
.concatMap(res => Observable.from(res.json()).take(5))
.concatMap((data) => this.getItem(data))
.subscribe(
data => {
console.log(data);
}
);
With the above code, the calls to getItem() will be synchronous, i.e there will only be one request to 'item/${itemId}' at a time, and in order
If you don't care about the order and want to speed them up by sending multiple requests at a time, change the second concatMap to mergeMap. Note that the requests (to item/itemId) will still be in order but there is no guarantee about the responses' order. You can supply the maximum concurrent requests as the third parameter to mergeMap (ignoring the second parameter by passing undefined or null).
.concatMap(res => Observable.from(res.json()).take(5))
.mergeMap((data) => this.getItem(data), null, 3)
passing 1 as the third parameter of mergeMap would be equivalent to concatMap