2
votes

I need to requests to load multiple movies by their ids. Each request returns a separate Observable. I have the following code:

getMoviesDataByImdbIds(ids: number[]):Observable<IMovie[]> {
    return Observable.zip(ids.map( id => {
        let params = new HttpParams().set('i', this.getIMDBIdString(id)).set('apikey', API_KEY);
        return this.http.get(API_URL, {params}).map( data => {
           return this.extractMovieData(data);
      })
    }));
}

Since there might be quite many ids, the whole operation takes quite a while. And the result is only returned once all the requests complete.

Is it possible to make output Observable emit the array every time a new request is complete? Like so:

1 complete -> [{movie1}]

2 complete -> [{movie1}, {movie2}]

3 complete -> [{movie1}, {movie2}, {movie3}],

etc.

2

2 Answers

2
votes

Scan operator can do this:

Rx.Observable.of(1, 2, 3, 4, 5)
  .scan((acc, v) => acc.concat(v), [])
  .subscribe(e => console.log(e))
<script src="https://unpkg.com/rxjs/bundles/Rx.min.js"></script>
0
votes

In case someone is interested how it worked in my scenario (in case of multiple asynchronous requests) here is how I changed my code (thanks to Oles Savluk's answer). Responsiveness of my app has enormously increased after this fix:

getMoviesDataByImdbIds(ids: number[]):Observable<IMovie[]> {
    return Observable.of(...ids).flatMap( (id) => {
      let params = new HttpParams().set('i',this.getIMDBIdString(id)).set('apikey', API_KEY);
      return this.http.get(API_URL, {params}).map( data => {
         return this.extractMovieData(data);
      })
   }).scan( (acc, movie) => {
       return acc.concat(movie);
   }, []);
}

Hope this will help someone.