typescriptrxjs

How should I make series of API calls with the middle one needing time?


I need to make a series of calls to an API. Here is what should happen:

This is what I have so far, but it does not work and does not include the third call.

this.firstCall(formData)
  .pipe(
    interval(3000)
    .pipe(
      switchMap(res => {
        this.secondCall(res)
          .pipe(
            catchError(error => {
              return of(null);
            })
        )
      }),
      takeUntil(res => res.code !== 'P'),
      timeout(21000)
    )
  )
  .subscribe({
    next: (data) => {
      console.log('SUCCESS DATA', data)
    },
    error: (error) => {
      console.log('PROCESSING ERROR', error)
    }
  })

Solution

  • We can use expand to recursively call the second API, until we achieve C (Complete), then we trigger the third API.

    I am assuming the third API does not return an object with the property code. I use this assumption to break the expand using EMPTY which immediately completes the stream.

    I use filter operator to block out the emissions where code is P.

    import { of, expand, switchMap, EMPTY, delay, filter } from 'rxjs';
    
    let i = 0;
    const firstAPI = () => of('123');
    const secondAPI = (id: any) => {
      console.log('second API called');
      i++;
      return i > 10 ? of({ code: 'C' }) : of({ code: 'P' });
    };
    const thirdAPI = (id: any) => of(`hello ${id}`);
    
    firstAPI()
      .pipe(
        switchMap((id: any) =>
          secondAPI(id).pipe(
            // we use expand to recurisvely call the second API.
            expand((val: any) => {
              // when the third API gets call use some property to break the expand loop
              if (!val?.code) {
                // immediately complete the stream
                return EMPTY;
              }
              // it is still processing so call the second api again with a small delay
              if (val.code === 'P') {
                return secondAPI(id).pipe(delay(500));
              } else if (val.code === 'E') {
                // an error has occoured, so we can throw an error, feel free to add a catchError in the stream.
                throw new Error('Some Error Occoured');
              } else {
                // the second API is completed so trigger the third API.
                return thirdAPI(id);
              }
            }),
            filter((item: any) => item?.code !== 'P')
          )
        )
      )
      .subscribe({
        next: (data: any) => {
          console.log('SUCCESS DATA', data);
        },
        error: (error: any) => {
          console.log('PROCESSING ERROR', error);
        },
      });
    
    
    

    Stackblitz Demo