Search code examples
angularrxjsobservable

Observable that manages a "request running" variable


In Angular, I'm currently writing a lot of Code repeatedly like this:

  • I have service classes that return an Observable<XYZ>, that are sending requests to my backend
  • In my components I want to make sure that a request is only running once at the same time. So if a request takes a bit longer and the user hits a button again and again it doesn't start a new request. So my component has some requestRunning variable and invoking the service then looks like this:
myFunction() {
  if (this.requestRunning) {
    return;
  }
  this.requestRunning = true;
  this.myService.callBackend().subscribe(
    success => {
      doSomething();
    },
    error => {
      this.requestRunning = false;
    },
    _ => { // complete
      this.requestRunning = false;
    }
  );
}

What I would prefer is something like an Observable which I can pass in the requestRunning variable, and that sets it to true if the Observable is subscribed to, and set to false if the Observable finishes (complete/error).

Does somebody know a cool and DRY solution for this?


Solution

  • you can use exhaustMap operator, this way you won't even need to maintain a variable to keep track of state, here the demo

    though you'll need to change the way you are adding the listener on your button:

      ngOnInit() {
        const btn = this.button.nativeElement;
    
        // this will produce outer observable
        fromEvent(btn, "click")
          .pipe(
            exhaustMap(() => {
              console.log("calling api...");
              // this will produce inner observable, so exhaustMap will wait for inner 
              // observable to complete, until then it any emissions from outer observables are going to be ignored.
              return this.appService.callApi();
            })
          )
          .subscribe({
            next: (apiResponse) => {
              console.log("api response recieved");
            }
          });
      }