2

I'm facing an issue currently, where a first search could take 5 seconds, and second one that takes 2 seconds, problem is, the first search which takes longer, will "erase" the results of the request that was done after, because the calls ends after.

I tried to read about

switchMap

in rxJs and tried to use it, but from what I try, it doesn't unsubscribe from previous request and erases the result.

There is probably something I'm doing wrong here but I can't exactly point out what is the issue.

Merge has 3 sources of change in the result ( pagination, sorting, or new search criteria ), and the call to sendSearchCriteria returns the data used.

sendSearchCriteria returns an Observable

is there anything that comes in mind for you, that I'm doing wrong ?

Thank you for your help,

private loadDogsResults = (filtersInformation: FilterSearchCriteria) => {
    merge(this.sort.sortChange, this.paginator.page, of(filtersInformation))
      .pipe(
        distinctUntilChanged(),
        tap(() => (this.isLoading = true)),
        switchMap(() => this.sendSearchCriteria(filtersInformation)),
        //mergeMap(() => this.sendSearchCriteria(filtersInformation)),
        map(data => this.formatResults(data)),
        finalize(() => (this.isLoading = false)),
        catchError(error => this.handleError(error)),
        takeUntil(this._onDestroy$)
      )
      .subscribe((result: any[]) => {
        if (result.length > 0) {
          this.setDisplayedColumns(result[0]);
        }
        this.isLoading = false;
      });
  }
Dinosan0908
  • 1,082
  • 2
  • 8
  • 19
  • What you are describing is _exactly_ the use case of switchMap, it cancels in-flight requests that are old and even if it doesn't it ignores their results. – Benjamin Gruenbaum Apr 26 '19 at 12:50
  • yes but that's the thing, it doesn't work as I'd like to, request is not cancelled and results are taken, even with the use of commented switchMap :/ – Dinosan0908 Apr 26 '19 at 12:52
  • 1
    Try to create an [mcve] on stackblitz.io - from a quick test with angular's http and switchMap things seem to work normally. Your princess is in another castle something else is wrong with your code. – Benjamin Gruenbaum Apr 26 '19 at 12:55
  • Note that `mergeMap` (which you have here instead of switchMap) absolutely will not work for this and will not cancel the event - this is by design. – Benjamin Gruenbaum Apr 26 '19 at 12:55
  • yes, when uncommenting switchMap I comment the mergeMap, which doesn't help :( – Dinosan0908 Apr 26 '19 at 12:57
  • a colleague sent me a stackblitz.io which works fine for the use case of switchMap, but unfortunately it doesn't help me to see clearer whats wrong in my code https://stackblitz.com/edit/rxjs-ui3che – Dinosan0908 Apr 26 '19 at 12:58
  • There is really nothing I can help you with if the stackblitz is of a working example - I already know switchMap works in stackblitz :] Sorry and may the debugging gods be in your favor. – Benjamin Gruenbaum Apr 26 '19 at 13:00
  • thanks :) I hope i'll manage to solve it ! – Dinosan0908 Apr 26 '19 at 13:21
  • What does `sendSearchCriteria` return or how does it create make the remote call? In order to make `switchMap` work in needs to return a subscription cat can be unsubscribed. – martin Apr 26 '19 at 14:10
  • @Dinosan0908 How does `loadDogsResults` get called? Is it called only once, or are you calling it multiple times (e.g. every time `filtersInformation` changes)? – seniorquico Apr 26 '19 at 15:06
  • thank you guys, solution found thanks to all the ideas ! – Dinosan0908 Apr 29 '19 at 07:49

3 Answers3

2

Which event triggers the search? This event should be the source of your Observable, that will be "piped" with switchMap operator.

As I can see here, if you call loadDogsResults() for each event, it won't work as you create a new Observable each time. of(filtersInformation) is an Observable which emits the filtersInformation value ONCE when the observable is subscribed, I don't think that it's the expected behavior.

HTN
  • 3,388
  • 1
  • 8
  • 18
1

You are doing a merge on 3 separate observables, which will result in you entering the pipe on 3 separate occasions, and firing off 3 separate calls to sendSearchCriteria. If what you want to do is call sortChange and page, get the results of both and then call sendSearchCriteria, you can...

private loadDogsResults = (filtersInformation: FilterSearchCriteria) => {
    forkJoin(this.sort.sortChange, this.paginator.page)
      .pipe(
        distinctUntilChanged(),
        tap(() => (this.isLoading = true)),
        switchMap(() => this.sendSearchCriteria(filtersInformation)),
        //mergeMap(() => this.sendSearchCriteria(filtersInformation)),
        map(data => this.formatResults(data)),
        finalize(() => (this.isLoading = false)),
        catchError(error => this.handleError(error)),
        takeUntil(this._onDestroy$)
      )
      .subscribe((result: any[]) => {
        if (result.length > 0) {
          this.setDisplayedColumns(result[0]);
        }
        this.isLoading = false;
      });
  }

However keep in mind in your subscribe you will only have access to the response from sendSearchCriteria.

  • Thank you ! Problem was more due to initialization but indeed the merge was an issue, thanks for the lead, cfr: posted solution ! – Dinosan0908 Apr 29 '19 at 07:49
0

A colleague helped me find the issue :) we tried forkJoin, combineLatest and multiple options but biggest issue was initialization/subscription done in an odd way.

we now created a searchTriggerSubject and added those lines

  private initializeSubscription() {
    this.searchTriggerSubscription = this.searchTriggerSubject.pipe(
      tap(() => (this.isLoading = true)),
      switchMap(() => this.sendSearchCriteria(this.claimsFilters)),
      map(data => this.formatResults(data)),
      catchError(error => this.handleError(error)),
      takeUntil(this._onDestroy$)
    ).subscribe((result: any[]) => {
    if (result.length > 0) {
      this.setDisplayedColumns(result[0]);
    }
    this.isLoading = false;
  });
  }

  private initializeSearchTriggerValues() {
    this.sort.sortChange.subscribe(() => this.searchTriggerSubject.next())
    this.paginator.page.subscribe(() => this.searchTriggerSubject.next())
    this.filtersSubscription = this.claimsFiltersService.getFilterValues()
    .pipe(
      filter(filters => filters !== null),
      tap((filter) => this.claimsFilters = filter)
    )
    .subscribe(() => this.searchTriggerSubject.next());
  }

the filters were coming form a service so this is what we did to avoid the issue.

Some refactoring was needed

Thanks a lot everyone for the different leads, having multiple subjects makes it easier

Dinosan0908
  • 1,082
  • 2
  • 8
  • 19