1

I have to make multiply sync webcalls, one after another if it's status is ok.

Horrible, but working code:

        this.api.post1().subscribe((data: any) => {
          if (data.status== 'OK') {

            this.api.post2().subscribe((data: any) => {
              if (data.status== 'OK') {

                this.api.post3().subscribe((data: any) => {
                  if (data.status== 'OK') {

                    this.api.post4().subscribe((data: any) => {
                      if (data.status == 'OK') {
                        // Do something
                      }else{
                        console.log('error: ' + data.status);
                      }
                    });

                  }else{
                    console.log('error: ' + data.status);
                  }
                });

              }else{
                console.log('error: ' + data.status);
              }
            });

          }else{
            console.log('error: ' + data.status);
          }
        });

I try use concatMap

const post1$ = this.api.post1();
const post2$ = this.api.post2();
const post3$ = this.api.post3();
const post4$ = this.api.post4();

from([post1$, post2$, post3$, post4$]).pipe(
      concatMap(a => a)
    ).subscribe(res => console.log(res));

But I need to check is each answer ok here. Is there a beautiful solution to this?

Alex
  • 39
  • 1
  • 8

3 Answers3

2

I would recommend you to use RxJS's pipeable operators, and handling the errors along the pipe chain. You can make use switchMap for the chaining of the requests, as well as using throwError to end the chain and emit error observables.

this.api.post1()
  .pipe(
    switchMap(res => {
      if (data.status === 'OK') {
        return of(this.api.post2());
      } else {
        console.log('error: ' + data.status);
        return throwError('Twos are bad');
      }
    }),
    switchMap(res => {
      if (data.status === 'OK') {
        return of(this.api.post3());
      } else {
        console.log('error: ' + data.status);
        return throwError('Twos are bad');
      }
    }),
    switchMap(res => {
      if (data.status === 'OK') {
        return of(this.api.post4());
      } else {
        console.log('error: ' + data.status);
        return throwError('Twos are bad');
      }
    }),
).subscribe(res => {
  console.log(res);
  // do the rest here
}, error => {
  // handle error
})
wentjun
  • 40,384
  • 10
  • 95
  • 107
-1

You can throw a generic error from your stream if any of them error out like below.

const post1$ = this.api.post1();
const post2$ = this.api.post2();
const post3$ = this.api.post3();
const post4$ = this.api.post4();

concat([post1$, post2$, post3$, post4$]).pipe(
  switchMap(data => {
    if (data.status !== "OK") {
      return throwError("Some error");
    }

    return of(data);
  })
).subscribe(res => console.log(res));

OR: If you need to know something specific about each of the observables, you can use concat but pipe the value through each of the endpoints before you get to the concat.

const handleResponse = (type: string) => 
    (responseObs) => responseObs.pipe(
      switchMap(data => {
        if (data.status !== "OK") {
          return throwError("Some error about " + type);
        }

        return of(data);
      })
    );

const post1$ = this.api.post1().pipe(handleResponse("Post 1"));
const post2$ = this.api.post2().pipe(handleResponse("Post 2"));
const post3$ = this.api.post3().pipe(handleResponse("Post 3"));
const post4$ = this.api.post4().pipe(handleResponse("Post 4"));

concat([post1$, post2$, post3$, post4$]).subscribe(res => console.log(res));
observingstream
  • 466
  • 3
  • 8
  • While it is a legit way of using exceptions, they should not be used as routing/branching solution because its a "stress" for the system takes resources and is much slower than any other solution. – tgralex Oct 14 '19 at 15:43
  • And it was not me who downgraded your answer :) – tgralex Oct 14 '19 at 16:28
  • Why would you not want the error? The server isn't returning a status code error, but an error still occurred. Also, saying it is a "stress" for the system and is much slower doesn't make sense since it is all RxJS and not actually throwing errors. – observingstream Oct 14 '19 at 17:32
-1

Here is another approach:

import { concat, of } from 'rxjs';
import { delay } from 'rxjs/operators';

var post1$ = of({status: "OK", data: 1});
var post2$ = of({status: "OK", data: 2});
var post3$ = of({status: "!OK", data: 3});
var post4$ = of({status: "OK", data: 4});

var runSubscribe = (
  data: {status: string, data: any}, 
  chainStatus: {index: number, hasError: boolean}) => {
  if( chainStatus.hasError){
    return;
  }
  chainStatus.index++;
  if (data.status !== 'OK') {
    console.log('error: ', data.status, data.data);
    chainStatus.hasError = true;
    return;
  }
  processData(chainStatus.index, data.data);
}

var processData = (index, data) => {
  // you should put your "Success" processing function based on index
  // I just randomly delay to up to 1000 msec
  const random = Math.random()*1000;
  delay(random);
  console.log(`${index} success:`, data, random);
}


const status = {
  index: -1,
  hasError: false
}
console.clear();
concat(post1$, post2$, post3$, post4$).subscribe(
  data => runSubscribe(data, status)
);

It will stop on 3-rd observable since its status is not "Ok".

see it here: https://stackblitz.com/edit/typescript-35hdyb

tgralex
  • 794
  • 4
  • 14
  • This actually doesn't work. All api calls will be made still. Please see the attached stackblitz which shows the subscribe method is called 4 times. https://stackblitz.com/edit/typescript-614wxs?file=index.ts. Also, your processData doesn't actually delay the observable stream because you aren't returning the operator. – observingstream Oct 14 '19 at 16:07
  • If you check results in console, which is hidden by default (right bottom side of screen), you'll see that my example by link is working and producing correct results. At least I see the result.: – tgralex Oct 14 '19 at 16:25
  • I agree that the console shows the correct values, but if you look at my stackblitz, you will see that with your example all 4 observables are still being called every time. You aren't stopping the 4th observable from being run instead it appears like you are just now showing the results in your code. – observingstream Oct 14 '19 at 17:25
  • I see what you are saying... yes, you are correct, while code is not executing, the call happens... I guess I'll need to play more to get a desired effect. – tgralex Oct 14 '19 at 19:05