0

Scenario :

  • Maybe it's something simple, but I can not find a solution.
  • I have a service "firstService" that returns an Observable of A[]. I have a service "secondService" that is passed an object A and returns an Observable . What I want at the end is an object B[].
  • Object A is different from object B.


Todo :

  • I want to avoid double subscription.


I have it programmed like this,
Although I know it is not correct due to the double subscription:

this.B = [];
this.services.firstService().subscribe(
elements => {
   elements.forEach (element => {
     this.services.secondService(element).subscribe(s => {
       this.B.push(s);
     })
   });
});

Which operators or combination of operators would improve the above?

0

1 Answer 1

2

Subscriptions inside subscriptions usually can be solved by using operators. What you are probably looking for is the mergeMap operator, which takes an emitted item from the so-called outer observable (in your case: this.services.firstService()) and maps it to a new inner observable, which in your case would be this.services.secondService(element).

Now, in your specific case, each emission of the outer observable causes the creation of multiple inner observables, due to elements being a list. You have multiple options to deal with that, but I would suggest to use the zip operator. It transforms an Observable<B>[] to an Observable<B[]>, which is exactly what you want.

Your example can be solved like this:

import {zip, Observable} from 'rxjs';
import {mergeMap} from 'rxjs/operators';

this.services.firstService().pipe(
    mergeMap(elements => 
       <Observable<B[]>> zip(...elements.map(element =>
            this.services.secondService(element)))
    ))
    .subscribe(elements => {
        this.B = elements;
});

More info on the used operators can be found here:

zip http://reactivex.io/documentation/operators/zip.html

mergeMap http://reactivex.io/documentation/operators/flatmap.html

Sign up to request clarification or add additional context in comments.

6 Comments

Please see my edit, zip operator takes only spreaded arrays :)
Thank you very much for the reply. Applying your solution proposal I get the following error: ERROR TS2345 Unable to assign an argument of type "(elements: A []) => OperatorFunction <B, {}>" to the type parameter "(value: A [], index: number) => ObservableInput <{}> " The type 'OperatorFunction <B, {}>' can not be assigned to the type 'ObservableInput <{}>'. The type 'OperatorFunction <B, {}>' can not be assigned to the type 'Iterable <{}>'. The property '[Symbol.iterator]' is missing in the type 'OperatorFunction <B, {}>'. I've tried, but I can not figure out what happens.
This lookes like the return type of zip cannot be inferred. Could you try casting it to the desired type, I'll add it in an edit.
Oh, I forgot: Make sure to include the correct zip. Import it from "rxjs", not from "rxjs/operators"
Thank you! It was indeed a problem with the import. It is not necessary to do the cast. Solved!
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.