3

I'm struggling a bit with coming up with a good solution for doing multiple http requests, based on the result of each of them. So far, I've been doing good with switchMap, as long as it's only 2 http requests. However, I've found out I need to perform 3, and I'm not sure how to do it with switchMap, or if there's a better solution for it.

Basically, I will await a response, and if it comes back empty, I want to perform a 2nd request, and if that comes back empty, I want to perform a 3rd request.

Let me try to give a simplified example of what I've gotten so far:

getData(): Observable<number> {
  const $subject = new Subject<number>();

  // loadData() takes in an id to specify what data to load
  service.loadData(1).pipe(
    switchMap(response => {
      if(response.length === 0) {
        return service.loadData(2);
      }
      else {
        return of(response);
      }
    }),
  ).subscribe(result => {
    $subject.next(result);
    $subject.complete();
  });
  return $subject.asObservable();

Now this works just fine, my issue is, that calling "loadData(2)", might also give back a response with length = 0, and in that case, I need to call loadData(3).

I tried having another switchMap within the switchMap, but that didn't seem to work properly. So my question is, how would I be able to check the length of the response of loadData(2), and if it's 0, return service.loadData(3) ?

Any help or tips are very welcome :)

2
  • take a look at this answer: stackoverflow.com/a/53560996/2050306 Commented Mar 13, 2020 at 15:18
  • @robert While this answer does look useful, I can't see how it takes the response of each observable into consideration - In my scenario, most cases I will only need to do the first call, while this suggestion seems to always perform all 3 calls Commented Mar 14, 2020 at 12:41

2 Answers 2

2

One straight forward and ugly way would be to subscribe to each call individually and check it's result.

getData(): Observable<number> {
  const $subject = new Subject<number>();

  service.loadData(1).subscribe(
    loadOneResponse => {
      if(loadOneResponse.length === 0) {
        service.loadData(2).subscribe(
          loadTwoResponse => {
            if(loadOneResponse.length === 0) {
              service.loadData(3).subscribe(
                loadThreeResponse => {
                  $subject.next(loadThreeResponse);
                  $subject.complete();
                },
                loadThreeError => {
                }
              );
            } else {
              $subject.next(loadTwoResponse);
              $subject.complete();
            }
          },
          loadTwoError => {
          }
        );
      } else {
        $subject.next(loadOneResponse);
        $subject.complete();
      }
    },
    loadOneError => {
    }
  );

  return $subject.asObservable();
}
Sign up to request clarification or add additional context in comments.

3 Comments

Yeah, I thought about something like this, but I feel like there should be a more elegant approach to this - I might end up doing this though, in case I don't find something better. Thanks for your suggestion though :)
You're welcome. Yeah, there might something more elegant, but sometimes the simple way too doesn't seem so bad.
Thank you, I was facing something similar and got a solution working from your idea. stackoverflow.com/a/63960012/738690
0

One possible solution is to use from with pipe

and takeWhile -> concatMap -> filter -> tap operators.

getData in service will receive an array of id numbers to try getting data for.

from will emit each id from the ids array.

concatMap does not subscribe to the next observable until the previous completes.

filter will ignore empty results.

takeWhile will cancel the whole stream once take becomes false

  getData(ids: number[]): Observable<number[]> {
    let take = true;
    return from(ids).pipe(
      takeWhile(() => take),
      concatMap(id => this.getDataFromApi(id)),
      filter(result => result.length > 0),
      tap(() => take = false)
    );
  }

Sample usage in the component:

  handleGetData() {
    console.log("component: getData");
    const ids = [1, 2, 3, 4];
    this.dataService.getData(ids).subscribe(data => {
      console.log("got data: ", data);
    });
  }

Working stackblitz

Comments

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.