61

Below is code from the Ngrx example: https://github.com/ngrx/example-app/blob/master/src/effects/book.ts My question is why in the first @Effect, it uses switchMap while the others use mergeMap. Is that because the first @Effect is dealing with network, and with the switchMap you can cancel the previous network request if it's running?

@Effect() search$ = this.updates$
    .whenAction(BookActions.SEARCH)
    .map<string>(toPayload)
    .filter(query => query !== '')
    .switchMap(query => this.googleBooks.searchBooks(query)
      .map(books => this.bookActions.searchComplete(books))
      .catch(() => Observable.of(this.bookActions.searchComplete([])))
    );


  @Effect() clearSearch$ = this.updates$
    .whenAction(BookActions.SEARCH)
    .map<string>(toPayload)
    .filter(query => query === '')
    .mapTo(this.bookActions.searchComplete([]));


  @Effect() addBookToCollection$ = this.updates$
    .whenAction(BookActions.ADD_TO_COLLECTION)
    .map<Book>(toPayload)
    .mergeMap(book => this.db.insert('books', [ book ])
      .mapTo(this.bookActions.addToCollectionSuccess(book))
      .catch(() => Observable.of(
        this.bookActions.addToCollectionFail(book)
      ))
    );


  @Effect() removeBookFromCollection$ = this.updates$
    .whenAction(BookActions.REMOVE_FROM_COLLECTION)
    .map<Book>(toPayload)
    .mergeMap(book => this.db.executeWrite('books', 'delete', [ book.id ])
      .mapTo(this.bookActions.removeFromCollectionSuccess(book))
      .catch(() => Observable.of(
        this.bookActions.removeFromCollectionFail(book)
      ))
    );
}
Derek Hill
  • 5,965
  • 5
  • 55
  • 74
Tuong Le
  • 18,533
  • 11
  • 50
  • 44
  • 10
    I came here looking for what the difference is, the clearest explanation I've come across is: `When you hear the word merge, think — use every­thing on all the streams aka. merge every­thing. Whereas when you hear the word switch, think — switch to using data on the newer stream` http://javascript.tutorialhorizon.com/2017/03/29/switchmap-vs-flatmap-rxjs/ – Michael Jun 06 '17 at 01:19

5 Answers5

101

You are correct; switchMap will unsubscribe from the Observable returned by its project argument as soon as it has invoked the project function again to produce a new Observable.

RxJs is incredibly powerful and dense, but its high level of abstraction can sometimes make code hard to understand. Let me debunk the marble diagrams and docs given by @Andy Hole a little and bring them up to date. You may find the marble syntax reference highly valuable to better understand rxjs operators from their tests (at least I found this missing/not highlighted enough in the official docs).

mergeMap

mergeMap

The first line in the diagram is the source Observable which emits (1,3,5) at different times. The second line in the diagram is the prototypical Observable returned by the project function i => ... passed to the .mergeMap() operator.

When the source Observable emits the item 1, mergeMap() invokes the project function with i=1. The returned Observable will emit 10 three times, every 10 frames (see marble syntax reference). The same happens when the source Observable emits item 3 and the project function creates an Observable that emits 30 three times. Note that the result of mergeMap() contains all three elements generated by each Observable returned from project.

switchMap

switchMap This is different with switchMap(), which will unsubscribe from the Observable returned by project as soon as it has invoked it again on a new element. The marble diagram indicates this with the missing third 30 item in the output Observable.

In the example you have given, this leads to the cancellation of the pending search request. This is a very nice but hard-to-get-right property, which you get for free by combining switchMap() with cancellable Observables returned by Angular's Http service. This can save you a lot of headaches without worrying about properly handling all the race conditions that typically occur with async cancellation.

Community
  • 1
  • 1
Johannes Rudolph
  • 35,298
  • 14
  • 114
  • 172
9

You are right.

As you can see, switchMap is used with search functionality. The searchbox in this example is programmed to basically emit a search request when the user enters text in the textbox (with a 350ms debounce or delay).

This means that when the user enters 'har', ngrx sends a search request to the service. When the user enters another letter 'r', the previous request is canceled (since we are not interested in 'har' anymore, but 'harr').

It is very nicely shown in the marble diagrams provided in another answer. In mergeMap, the previous Observables are not canceled and therefore '30' and '50' are mixed together. Using switchMap, only the 5s are emitted, because the 3's are canceled.

Alexander Ciesielski
  • 10,506
  • 5
  • 45
  • 66
3

mergeMap

Projects each source value to an Observable which is merged in the output Observable.

Maps each value to an Observable, then flattens all of these inner Observables using mergeAll.

enter image description here

switchMap

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

Maps each value to an Observable, then flattens all of these inner Observables using switch.

enter image description here

Source: ES6 Observables in RxJS

Community
  • 1
  • 1
Andy Hoyle
  • 668
  • 6
  • 8
0

You don't want an API save data request to cancel. That is why you would use mergeMap. A search query can be thrown away, no loss of data, and the user might be editing their query and are not interested in the data for the old one. Hence switchMap.

Derek Kite
  • 1,767
  • 13
  • 15
0

Yes, if you are no longer concerned with the response of the previous request when a new Input arrives switchMap is a suitable operator than mergeMap.

Iswarya
  • 1
  • 1