0

In the below code, this.busyCount++ is called just once per call to my get method but the this.busyCount-- inside tap() is being called multiple times. This is causing isBusy to always be false.

What is the correct way to increment and decrement busyCount to find out if there is an active http request.

The showBusy parameter determines if loading spinner is displayed or not. I'm not sure how I would pass this to an interceptor or I could use the approach here.

I've tried using finalize operator to decrement but I faced the same issue.

@Injectable()
export class DataService {    

    constructor(private http: HttpClient) { }

    public busyCount = 0;
    public get isBusy(): boolean { return this.busyCount > 0; }

    get<T>(relativeUrl: string, showBusy: boolean = true): Observable<T> {
        if (showBusy)
            this.busyCount++;
        return this.http.get<T>(`${this.baseURL}${relativeUrl}`)
            .pipe(
                tap(s => {
                    if (showBusy)
                        this.busyCount--;
                }),
                catchError((err: HttpErrorResponse) => {
                    if (showBusy)
                        this.busyCount--;
                    return Observable.throw(err)
                })
            );
    }
}
pradhyo
  • 157
  • 1
  • 2
  • 19

3 Answers3

1

According to the examples in the documentation, using tap with http.get may give you more events than just the success response you're expecting. For one thing, it will emit on both success and failure, and I suspect on redirects as well.

If you restrict your decrement operation to only occur when the event is HttpResponse I believe you'll get the behavior you're looking for:

return this.http.get<T>(/*...*/).pipe(
    tap(event => {
        if (showBusy && event instanceof HttpResponse)
            this.busyCount--;
    }),
    catchError /* ... */
}
Myk Willis
  • 12,306
  • 4
  • 45
  • 62
0

You are returning an observable from the service. Each time you create this observable you increment the busy count but no http call has been made until something subscribes to the observable. So your busy count goes up by one but it is not necessarily busy yet.

Each time something subscribes to the observable the busy count will go down as the request finishes.

Without seeing how you use the observables it is not possible to predict how many times you subscribe to the same observable but you only ever increment once and decrement every time a subscription fires.

Having a single flag for all activity seems undesirable to me anyway.

You can read my article about Angular state management with my RxCache library and see the pattern I use here https://medium.com/@adrianbrand/angular-state-management-with-rxcache-468a865fc3fb

Adrian Brand
  • 20,384
  • 4
  • 39
  • 60
0

This is hacky but works so we ended up using this

@Injectable()
export class DataService {    

    constructor(private http: HttpClient) { }

    public busyCount = 0;
    public get isBusy(): boolean { return this.busyCount > 0; }

    get<T>(relativeUrl: string, showBusy: boolean = true): Observable<T> {
        return this.handleHttpRequest(this.http.get<T>(`${this.baseURL}${relativeUrl}`), showBusy);
    }

    private handleHttpRequest<T>(httpResponse$: Observable<T>, showBusy: boolean = true): Observable<T> {
        return Observable.from([0])
            .do(() => {
                if (showBusy) {
                    this.busyCount++;
                }
            })
            .switchMap(() => {
                return httpResponse$;
            })
            .pipe(
                catchError((err: HttpErrorResponse) => {
                    return Observable.throw(err)
                }),
                finalize(() => {
                    if (showBusy) {
                        this.busyCount--;
                    }
                })
            );
    }

}
pradhyo
  • 157
  • 1
  • 2
  • 19