Subscribe to nested observable

I have an application that makes one HTTP request to get a list of items, and then makes an HTTP request for each item in the list to get more details about each item. Effectively:

class ItemsService { fetchItems() { return this.http.get(url) .map(res => res.json()) .map(items => items.map(this.fetchItem(item))); } fetchItem(item: Item) { this.http.get(`${url}/${item.id}`) .map(res => res.json()); } } 

Then I will do something like itemsService.fetchItems().subscribe(items => console.log(items)) , but in the end I get an array of observables (each answer from fetchItem ). I also need to subscribe to each of the internal observables so that the fetchItem request really works.

I also tried using flatMap instead of a map, but in this case it seems to have the same result. Is there a way for nested observables to be signed?

+5
source share
3 answers

I would do the following:

 function mockRequest() { return Observable.of('[{"id": 1}, {"id": 2}, {"id": 3}]'); } function otherMockRequest(id) { return Observable.of(`{"id":${id}, "desc": "description ${id}"}`); } class ItemsService { fetchItems() { return mockRequest() .map(res => JSON.parse(res)) .concatAll() .mergeMap(item => this.fetchItem(item)); } fetchItem(item: Item) { return otherMockRequest(item.id) .map(res => JSON.parse(res)); } } let service = new ItemsService(); service.fetchItems().subscribe(val => console.log(val)); 

See the demo version: http://plnkr.co/edit/LPXfqxVsI6Ja2J7RpDYl?p=preview

I use the trick with .concatAll() to convert an array of objects like [{"id": 1}, {"id": 2}, {"id": 3}] to separate values ​​that come out one after another {"id": 1} , {"id": 2} and {"id": 3} (currently this is an undocumented function). Then I use mergeMap() to get their contents in a separate request and combine it into a chain of statements.

This plnkr example prints to the console:

 { id: 1, desc: 'description 1' } { id: 2, desc: 'description 2' } { id: 3, desc: 'description 3' } 
+3
source

The problem that you probably ran into is that you have not ironed enough.

flatMap or mergeMap will smooth out Observables , Promises , Arrays , even generators (don't quote me on the latter), just about what you want to throw at it.

So, when you do .flatMap(items => items.map(item => this.fetchItem(item)) , you really just do Observable<Array<Item>> => Observable<Observable<Item>>

When you just make a map , you do Observable<Array<Item>> => Observable<Array<Observable<Item>>> .

What you need to do is first smooth the array and then smooth each request:

 class ItemsService { fetchItems() { return this.http.get(url) .map(res => res.json()) // Implicitly map Array into Observable and flatten it .flatMap(items => items) // Flatten the response from each item .flatMap((item: Item) => this.fetchItem(item)); } } 

Now the above works if you do not mind that each answer of each element is individual. If you need to get all the items, you should use forkJoin for all the internal values, but you still need a flatMap in to smooth out the resulting internal value:

 fetchItems(): Observable<Response[]> { return this.http.get(url) .map(res => res.json()) .flatMap(items => { const requests = items.map(item => this.fetchItem(item)); return Rx.Observable.forkJoin(requests); }); } 
+2
source

You can split an array of elements before the line that calls this.fetchItem . You can use mergeMap in Observable, whose value is an array, and each element will be emitted individually.

 fetchItems() { return this.http.get(url) .map(res => res.json()) .mergeMap(arrItem => this.fetchItem(arrItem)); } 

Edit: I think I should have given more explanations. mergeMap is synonymous with flatMap in rxjs. Typically, you use flatMap when your projection function returns Observable, but it will also smooth arrays, so when you call mergeMap, each element will be returned individually, I thought that was what the OP wanted to achieve. I also realized that you can combine the call to mergeMap and the last call to map , since the projection for mergeMap will be called for each element in the array, I made changes to the code above.

-1
source

Source: https://habr.com/ru/post/1258779/


All Articles