Home > Blockchain >  rxjs: Awaiting an httpGet call with pipes
rxjs: Awaiting an httpGet call with pipes

Time:03-23

This issue has popped up a couple of times, and every time it does I scramble to figure out how to do it the right way, since I can never seem how to find how I did it last time when I need it.

This time it came up because I realized I need to do an extra httpGet call when processing a previous httpGet call:

http.getWrapper<T>(whatever)
  .pipe<T, T>(map<T, T>(res => getWrapper2<T, T>(res)) // New function, returns the same type it was given
  .pipe<T, U>(oldStuff)

The issue that I'm having is that previously, without the extra get call, my oldStuff handler received a T that it could handle without issue. But the new get call returns an Observable<T>, and I always find it confusing to figure out how to tell the pipe chain to wait for the get to finish, and use the final value. Anything I can think of doing, which mostly seems to be variations of toPromise() and async/await, just move the extra layer between me and my data around, and I can't quite seem to get to it. For some reason inserting this second get just always seems to be more confusing than the first get was, and I don't know if I need a structure change or it's just my thinking is off.

To make things even more confusing, the extra layer of processing is only sometimes needed:

.pipe(map(res => condition(res) ? getWrapper2(res) : res))

What can I do to await and flatten this so I can pass the new get result to the rest of the pipe chain?

CodePudding user response:

you are looking for high order observables

the most popular operators: mergeMap, switchMap, concatMap

These operators should return observable and in the next step you will receive data


import { of } from 'rxjs';
import { map, mergeMap } from 'rxjs/operators';

const initialApiCall = () => of('initialApiCall');
const extraApiCall = (res) => of(res   '_'   'extra result'); // getWrapper2
const condition = (res) => !res;

initialApiCall()
  .pipe(
    mergeMap((res) => (condition(res) // checking condition
     ? extraApiCall(res) // api call
     : of(res)) // should convert privitive data to observable
    ),
    map((data) => console.log(data)) // here you will have result of `extraApiCall` or `res`
  )
  .subscribe();

DEMO:

https://stackblitz.com/edit/rxjs-suryq3?file=index.ts

CodePudding user response:

 url_users = 'https://jsonplaceholder.typicode.com/users';
 url_posts = 'https://jsonplaceholder.typicode.com/posts';

  this.http
      .get(this.url_users)
      .pipe(
        tap((res) => console.log('users- ', res)),
        concatMap((users) => this.http.get(this.url_posts   '/'   users[3].id))
      )
      .subscribe({
         next: (posts) => {
             console.log(posts);
         },
         error: (err) => console.log(err),
      });
  • concatMap subscribes to the inner Observable (In our case 2nd http.get) and sends its output to the result Observable
  • concatMap will wait for previous HTTP Observable to complete before mapping the new value to an HTTP Observable, subscribing to it and
    therefore triggering the next get

you will find more about Higher-Order RxJs Mapping Operators: switchMap, mergeMap, concatMap (and exhaustMap)

  • Related