RxJS Observable returning array, run another function with each array iteration

16,527

Solution 1

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

Solution 2

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)
})
Share:
16,527
gregor
Author by

gregor

Updated on June 05, 2022

Comments

  • gregor
    gregor almost 2 years

    I have a function getNews(), which basically returns angular's http.get request. Result of request is array of Id's. I would like to iterate through this array I got and run another http.get request (function getItem(id)), which will then return single Id's object, received from server.

    I've tried using it like so:

      getLatest() {
        return this.http.get('all_news_url')
        .map(res => res.json())
        // I even tried creating Observable from array and get only 5 elements
        //.map(res => Observable.from(res.json()))
        //.take(5)
        .map((data) => this.getItem(data))
        .subscribe(
          data => {
            console.log(data);
          }
        )
      }
    
      getItem(itemId: any): Observable<any> {
        console.log('item id', itemId);
    
        return this.http.get(this.generateUrl(`item/${itemId}`))
        .map(res => res.json());
      }
    

    Obviously, this doesn't work. parameter to getItem() function always gets passed as whole array of Id's.

    Thank you everyone for participating in this question.

  • gregor
    gregor almost 8 years
    This is exactly what I was looking for. There's just one little typo, at first part of code you posted, there's no need for .map(res => res.json()). Thank you very much.
  • Can Nguyen
    Can Nguyen almost 8 years
    You're right @gregor. I copied and pasted without paying much attention ;)