Observables : Cancel previous http request on new subscription call

26,689

Solution 1

I would use a subject to keep everything reactive. In your template html listen to change events and emit a new value to the subject.

 <searchBar (change)="search$.next($event.target.value)" />

then in your component:

  this.subscription = this.search$.pipe(
     debounceTime(800), 
     distinctUntilChanged(),
     switchMap(searchText=>http.post('api_link', {searchText})
    }).subscribe(response=>{
       this.response = response.
    });

The switchMap will cancel any HTTP request that hasn't completed if a new value is emitted through the subject. You can play with the debouneTime to see what feels right for you. Lastly, make sure you unsubscribe from your subject in the ngOnDestroy, this will prevent any memory links and keep everything nice and perfromant.:

ngOnDestroy(){
   this.subscription.unsubscribe();
}

Suresh's answer has a distinctUntilChanged() which is an excellent addition to the solution. I'm adding it but want to give credit to his answer below. This is a benefit because if I search for egg the request is made. But then I add an s the end of egg and change my mind before the debounce is completed another duplicate HTTP post with a search of egg will not be made.

Solution 2

You need to use debounceTime and switchMap operator.

this.searchBar.on('change', () => {

    of(this.serachBar.searchText).pipe(
       debounceTime(400),
       distinctUntilChanged(),
       switchMap((text)=> {
          return http.post('api_link', {searchText: text}).map(resp => {
            return resp['Result'];
          });
        });
    ).subscribe(response=> console.log(response));

});
Share:
26,689

Related videos on Youtube

D_S_X
Author by

D_S_X

Updated on November 10, 2021

Comments

  • D_S_X
    D_S_X over 2 years

    I am working on a search functionality for my project. Once the user types anything on the search bar; on any change in search text I'll be sending the text to backend for validation and receive a response (error or no error in text):

    this.searchBar.on('change', () => {
    
        http.post('api_link', {searchText: 
           this.serachBar.searchText}).subscribe(resp => {
                this.resp = resp['Result'];
           });
        })
    

    Now as the user continuously types in the search bar, multiple validation responses are incoming through back-end. However, on any new change only the latest subscription is valid and any previous call to api is useless.

    Is there any way I can cancel the previous subscription on any new call to api using subscribe?

    Note: It might seem possible to just wait for all responses but I am also going to display the responses below the search bar(showing a loader till then). So, rather than transition between various response states I want loader to keep loading until the latest response is available.

    • jonrsharpe
      jonrsharpe over 5 years
      Look into switchMap and debounce.
    • Yousef khan
      Yousef khan over 5 years
      you can use unsubscribe() on subscription for every new change. This way old http request will be cancelled and only latest subscription will be valid
    • Florian
      Florian over 5 years
      a great source that could help you in future : learnrxjs.io
    • martin
      martin over 5 years
  • D_S_X
    D_S_X over 5 years
    Actually my element is dynamically created in typescript, so i can't add change event listener in template. Whats the way to do this in ts only? (as mentioned in the question i have a change event listener in ts for this)
  • JoshSommer
    JoshSommer over 5 years
    @VSharma this is probably out of the scope of the original question. However, how are you creating the element dynamically?
  • JoshSommer
    JoshSommer over 5 years
    if you have the search bar class in your code you can subscribe to the change event emitter directly. so: this.searchBar.change.pipe( instead of this.search$.pipe( this is because Angular's Event Emitter extends RxJS's Subject. I thought I read at one time the Angular team didn't recommend subscribing to Event Emitters directly. But I could be mistaken, you may want to some digging around to confirm this. This isn't a situation I've typically encountered so I haven't had to worry about it.
  • chirag sorathiya
    chirag sorathiya almost 3 years
    Can we do this in interceptor ?