javascriptangulartypescriptrxjsfork-join

forkjoin using javascript object or sourcesObject implementation


I have a piece of code that looks like this

getInformations().subscribe(
    informations => {
        let subs = [];
        for (const information of informations) {
            subs.push(getOtherDetails(information.id));
        }
        forkJoin(subs).subscribe(response => {
           //How can I Associate Information Id With The Response
            howToAssociateIdWithResponse();
     }}
);

Situation - I want to tie the responses of my second call with the ids of my first call, but I am running into issues.

Attempted - I tried the following but that seems to be throwing error

let subs: {[x:number]: Observable<any>}[] = [];
subs.push({information.id: getOtherDetails(info.id)}) 

but when I subscribed I got an error stating You provided an invalid object where a stream was expected. You can provide an Observable, Promise, Array, or Iterable.


Update - 1 After following @BizzyBob suggestion, the code looks like the following but my other logic is running before the subscription completes it's job. Here is what I mean

async ngOnChanges(){
    await getData(); //How to make sure below method only executes when this is really done.
    await useInformationReceivedFromgetData(); 
}
async getData(){
  getInformations().subscribe(
    informations => {
      let subs = [];
      for (const information of informations) {
         subs.push(getOtherDetails(information.id).pipe(
            map(data => ({ id: information.id, data })) // <---
         ));
      }
      forkJoin(subs).subscribe(objects => {           
         objects.forEach(({id, data}) => { /* saved to an arrray */ });
      });
   }

);
}

Solution

  • You can make each "getOtherDetails observable" emit an object with the id and response data:

    getInformations().subscribe(
       informations => {
          let subs = [];
          for (const information of informations) {
             subs.push(getOtherDetails(information.id).pipe(
                map(data => ({ id: information.id, data })) // <---
             ));
          }
          forkJoin(subs).subscribe(objects => {           
             objects.forEach(({id, data}) => { /* use id and data here */ });
          });
       }
    );
    

    Note, you can simplify your code by using .map() instead of creating a subs array and pusing to it:

    getInformations().subscribe(
       informations => {
          const subs = informations.map(
             ({id}) => getOtherDetails(id).pipe(map(data => ({ id, data })))
          );
          forkJoin(subs).subscribe(responses => {           
             responses.forEach(({id, data}) => { /* use id and data here */ });
          });
       }
    );
    

    Also, putting subscribes inside of subscribes is bad news. You'd be better off to use a Higher Order Mapping Operator that will handle an "inner subscription" for you. In this case, we can use switchMap to handle subscribing / unsubscribing to your forkJoin observable:

    getInformations().pipe(
       map(informations => informations.map(
          ({id}) => getOtherDetails(id).pipe(map(response => ({ id, data })))
       ),
       switchMap(requests => forkJoin(requests))
    ).subscribe(
       responses => responses.forEach(({id, data}) => { ... })
    );