1

I use the following code in an angular app. I used the RxJS map call similar to how array map is used. After reading about RxJS switchmap operator, I'm not sure whether I should use map or switchmap. Should I use switchmap so the observable which is returned from the http call is closed so there is no memory leak?

getPeopleForTypeahead(term: string): Observable<IPersonForTypeahead[]> {

    var peopleUrl = `https://localhost:5001/api/peoplesearch?name=${term}`;

    return this.http.get<any>(peopleUrl)
      .pipe(
        map(pl => {
          return this.peopleAsFlattened(pl.peopleList).reduce((p, c) => p.concat(c));
        }),
        catchError(this.handleError('getPeopleForTypeahead', []))
      );
  }

  peopleAsFlattened = (pla: IPeopleList[]) => {
    return pla.map(pl => pl.people.map(p => {
      return {
        id: p.id,
        fullName: p.fullNames[0].firstName + " " + p.fullNames[0].lastName
      };
    }));
  }
wonderful world
  • 10,969
  • 20
  • 97
  • 194

3 Answers3

4

map and switchMap have completely different purposes:

  • map - transform the shape of an emission
  • switchMap - subscribe to an observable and emit its emissions into the stream

map

Use map when you want transform the shape of each emission. Ex: emit the user name property, instead of the entire user object:

userName$: Observable<string> = this.service.getUser(123).pipe(
    map(user => user.name)
);

switchMap

Use switchMap when you want to map an emission to another observable and emit its emissions. Ex: You have an observable of some id and want to emit the resource after fetching it:

user$: Observable<User> = this.userId$.pipe(
  switchMap(id => this.service.getUser(id)),
);

When user$ is subscribed to, the user returned from service.getUser(id) is emitted (not the userId string).

BizzyBob
  • 12,309
  • 4
  • 27
  • 51
  • In this example, is `userId$` called outer observable and whatever observable returned by the `switchMap` called inner observable? – wonderful world May 22 '22 at 15:17
  • Can't the `map` method also return `this.service.getUser(id)` like the `switchMap` does? – wonderful world May 22 '22 at 15:21
  • 1
    Yes, you are correct about terms inner observable and outer observable. As for having `map` return `this.service.getUser()`, you could do that however, `map` will emit the observable, where as `switchMap` will internally subscribe to the inner observable and emit the emissions from the inner observable. Usually it's not helpful to emit an observable, you usually want the value from the inner observable. – BizzyBob May 22 '22 at 15:34
  • 1
    Don't both scenarios emit `Observable` which can be subscribed? Or the first scenario return `Observable>` and the second scenario `Observable`? – wonderful world May 22 '22 at 15:45
  • 1
    https://stackblitz.com/edit/rxjs-map-vs-switchmap-bb?devtoolsheight=100&file=index.ts – BizzyBob May 22 '22 at 15:46
  • 1
    Nope, check out the above example stackblitz which shows the emission types for map vs switchMap. – BizzyBob May 22 '22 at 15:46
  • 1
    You may find this article about "[Higher Order Mapping Operators](https://blog.angular-university.io/rxjs-higher-order-mapping/)" helpful. – BizzyBob May 22 '22 at 15:48
1

switchMap is not interchangeable with the map operator, nor vise versa. Although both of them has to do with mapping (as their names suggest), they have two separate use-cases.

In your particular case, the map operator is the way to go.

When to use switchMap?

You can only use switchMap(cb) when you check all these requirements:

  1. Your callback function, cb, passed into switchMap returns an observable, observable$.

    If your cb (callback function) does not return an observable, you should look into operators that don't handle higher-level observables, such as filter and map (what you actually needed); not operators that handle higher-level observables such as concatMap and well, switchMap.

  2. You want to execute your cb sequentially before the next operation down the pipeline (after switchMap) executes.

    Maybe you want to run logic inside of cb, and optionally get the return value of cb after executing, so that you can pass it down the pipeline for further processing, for example.

  3. When you want to "discard" what will happen to cb's execution and re-execute cb every time the source observable (the thing that trickles down to switchMap(cb)) emits a new value/notification.


Applying what we hopefully learned, we know that your cb:

pl => {
    return this.peopleAsFlattened(pl.peopleList).reduce((p, c) => p.concat(c));
}

returns a plain JavaScript array; not an observable. This takes using switchMap out of the question since it violates the first requirement I made up above.

Hopefully that makes sense. :)

Christian
  • 553
  • 4
  • 16
0

We use switchMap when the source observable is a hot observable. In which case you prefer the behaviour that cancel the succeeding observable when source emits.

In your code, you source is a one-off http call which means it will not emit multiple times and the follow up action is not executing observable but to mutate an array. There is no need to use switchMap

Fan Cheung
  • 10,745
  • 3
  • 17
  • 39