skip to Main Content

Below is 2 snippets one with map, and second one with switchMap.

The working of map is understandable:

of('foo', 'bar')
  .pipe(map((val) => sanitizer(val)))
  .subscribe((val) => console.log('value:', val));

function sanitizer(val: string) {
  return val;
}

Output:

value: foo
value: bar

Now when using switchMap, only the last value is outputting. Why is that?

My assumption was that map is for sync operations and switchMap is for async operations (returning observables/promises).

of('foo', 'bar')
  .pipe(switchMap((val) => sanitizer(val)))
  .subscribe((val) => console.log('value:', val));

async function sanitizer(val: string) {
  return val;
}

Output:

value: bar

Here is stackblitz: https://stackblitz.com/edit/rxjs-acjgon?file=index.ts

3

Answers


  1. switchMap cancels the previous observable (switch to the new one), when a new values comes in, if you want to preserve the order then use concatMap – this will first resolve the first observable, then resolve the second! (order is preserved) (Special property of concatMap)

    of immediately emits one after another, so the first does not get resolved.

    of('foo', 'bar')
      .pipe(concatMap((val) => sanitizer(val)))
      .subscribe((val) => console.log('value:', val));
    
    async function sanitizer(val: string) {
      return val;
    }
    
    Login or Signup to reply.
  2. From the RxJS docs on switchMap:

    Projects each source value to an Observable which is merged in the output Observable, emitting values only from the most recently projected Observable.

    Remember that RxJS implements a push-based model. When switchMap receives a value, it begins processing it immediately. If a new value arrives during processing, switchMap will abandon the current Observable or Promise and switch to the new one.

    This behavior is especially useful in scenarios with frequent event updates, such as typing in a search box where only the latest input should fetch results:

    input$.pipe(
      switchMap(input => http.get(`/api/search?q=${input}`))
    ).subscribe(result => console.log(result));
    

    However, for your case where each input must be addressed sequentially, concatMap is more appropriate as it processes values one after the other without interruption.

    Hope this helps!

    Login or Signup to reply.
  3. Because of emits synchronously, and switchMap unsubscribe current inner observable at the moment it receives a new emission from the outer one.
    Being those two emissions synchronous, it’s like it receives them "together", then discarding the first one and using just the second for its projection function.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search