1

What's the best way to call a subscription wait for result to start another subscription? Something similar to await in javascript, I need the result of the first call to start the next call. I have this:

this.apiService.getAllData()
            .subscribe(
                ( response: any ) => {

                        this.allData = response['data'];
                        this.allData.forEach(function (id) {
                            this.apiService.getDataDetail(id)
                                .subscribe((detail: any) => {
                                    console.log('detail: ', detail);
                                });
                        });
                },
                err => {
                    console.log('Something wrong...');
                }
            );

My problem is, the call subscription inside the first subscription do not recognize the service . This warning show me by my editor

Potentially invalid reference access to a class field via 'this.' of a nested function 
BryGom
  • 649
  • 1
  • 11
  • 21
  • 2
    as a quick response about the error itself (you could use rxjs operators to avoid nested subscirption), it comes from 'forEach', you should use an arrow function inside it to keep the 'this' reference – Gérôme Grignon Apr 07 '20 at 09:35

3 Answers3

2

If I am understanding what you are trying to accomplish:

  • Load all data from the API
  • Then, load each individual record from the API (using the data you received from the first API request)

To do this you will want to use the switchMap() operator and probably the forkJoin() or combineLatest() functions.

/* needed imports */
import { combineLatest, forkJoin } from 'rxjs';
import { tap, switchMap } from 'rxjs/operators';

/* load all data */
this.apiService.getAllData().pipe(

  /* switchMap will "switch" the observable your subscriber receives */
  switchMap((response: any) => {
    this.allData = response['data'];

    /* we will map allData to an observables (ie: api requests) */
    /* try using an arrow function here to avoid `this` conflicts */
    const arrayOfObservables = this.allData.map(data => {
      return this.apiService.getDataDetail(id).pipe(
        tap(detail => console.log('detail: ', detail))
      );
    });

    /* switchMap needs to return an observable */
    return forkJoin(arrayOfObservables); 
    // could also use combineLatest(arrayOfObservables)
  })
).subscribe(
  (data: any[]) => {
    /* important to note that data will be an array of all the 
      individual details received from the api */
    console.log('received data: ', data);
  },
  err => console.log('Something wrong...', err)
);

It is important to note that forkJoin will not emit a value until all the observables in the array complete. This is probably your desired behavior since you are making http requests which always complete if you are using Angular.

If you want to emit details to your subscriber as they come in and not when all of them complete, then you may need something like a mergeMap(). That is more complex, and it doesn't look like that is what you want here (I could be wrong though).

EDIT: See my answer here for a mergeMap/concatMap example to show how to implement it. My answer also addresses observable clean up to avoid memory leaks.

DJ House
  • 1,307
  • 1
  • 11
  • 14
1

You can use RXJS for that. There is an operator call Switch map

this.apiService.getAllData()
  .pipe(
    switchMap(( response: any ) => {

                        this.allData = response['data'];
                        this.allData.forEach(function (id) {
                            this.apiService.getDataDetail(id)
                                .subscribe((detail: any) => {
                                    console.log('detail: ', detail);
                                });
                        });
                },
                err => {
                    console.log('Something wrong...');
                })
  )
  .subscribe();

get more details from https://www.learnrxjs.io/learn-rxjs/operators/transformation/switchmap. And there are many RXJS operators and you can easily work with reactive programming using those

PushpikaWan
  • 2,437
  • 3
  • 14
  • 23
1

This is just a hack, I used to do it before knowing better methods -

this.apiService.getAllData()
            .subscribe(
                ( response: any ) => {

                        this.allData = response['data'];
                        that = this
                        that.allData.forEach(function (id) {
                            that.apiService.getDataDetail(id)
                                .subscribe((detail: any) => {
                                    console.log('detail: ', detail);
                                });
                        });
                },
                err => {
                    console.log('Something wrong...');
                }
            );

You should prefer other methods over this.

Krishna
  • 6,107
  • 2
  • 40
  • 43