sai umesh
sai umesh

Reputation: 7

switchMap doesn't cancel previous http request

switchMap doesn't cancel previous http request. Also tried with exhaustMap but didn't help. here's gist for the same https://gist.github.com/saiumesh-letznav/a33ac54444ba707b9dbb172271b518fe

import { Subject } from 'rxjs';
import { switchMap } from 'rxjs/operators';
import Axios from 'axios';

const baseUrl =
  'https://api.github.com/search/repositories?q=golang+language:go&sort=stars&order=desc';

class GitService {
  private $obs: Subject<void> = new Subject();

  constructor() {
    this.$obs
      .pipe(switchMap((_) => Axios.get(baseUrl)))
      // tslint:disable-next-line:no-console
      .subscribe((res) => console.log({ res }));
  }

  public getGOlangRepos() {
    this.$obs.next();
  }
}

const instance = new GitService();

instance.getGOlangRepos();
instance.getGOlangRepos();
instance.getGOlangRepos();
instance.getGOlangRepos();

Upvotes: 1

Views: 2433

Answers (2)

Nicholas Tower
Nicholas Tower

Reputation: 84982

If you want to actually cancel the axios request (rather than letting it continue but ignoring its result), then you can't simply switchmap to the promise returned by axios.get. Promises aren't cancellable, and while axios does support cancellation, rxjs has no idea how axios's cancellation works. So you'd need to implement the cancellation yourself.

Outside of rxjs, here's how you can make an axios request and then cancel it later:

const cancelToken = Axios.CancelToken.source();
const promise = Axios.get(someUrl, { cancelToken: cancelToken.token };

// ... then later to cancel it:
cancelToken.cancel();

If you want rxjs to be able to use this cancellation logic , you'll need to create a custom observable. If this is something you think you'll be doing often, i'd recommend moving it into a helper function. Something like this:

function getAsObservable(url) {
  return Observable.create(function(observer) {
    const cancelToken = Axios.CancelToken.source();
    Axios.get(url, { cancelToken: cancelToken.token })
      .then(result => {
        observer.next(result);
        observer.complete();
      }, err => {
        if (axios.isCancel(err)) {
          observer.complete();
        } else {
          observer.error(err);
        }
      })

    return () => cancelToken.cancel();
  });
}

And then your code can become:

this.$obs
 .pipe(switchMap((_) => getAsObservable(baseUrl)))
 .subscribe((res) => console.log({ res }));

Upvotes: 8

Wand Maker
Wand Maker

Reputation: 18762

You can avoid multiple http calls by using debounceTime so that you add a delay before making http call. Once an http call is made, it will continue to be processed by server even if the subscription is cancelled in client app.

For example - below code will wait for 500ms before making http call.

constructor() {
 this.$obs
  .pipe(
     debounceTime(500),
     switchMap((_) => Axios.get(baseUrl))
  )
  // tslint:disable-next-line:no-console
  .subscribe((res) => console.log({ res }));
}

Upvotes: -1

Related Questions