实现无闪烁延迟Angular Spinner:HttpInterceptor代码异常排查
Fix: Show Spinner Only Once for Concurrent HTTP Requests in Angular HttpInterceptor
The issue with your current implementation is that each HTTP request independently triggers the spinner's show/hide logic, leading to multiple true/false logs and unwanted spinner flickers. To fix this, we need to track the number of active requests globally and only toggle the spinner when the first request starts and the last request finishes.
Step-by-Step Solution
Here's the revised interceptor code with a request counter to manage the spinner state correctly:
import { Injectable } from '@angular/core'; import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpResponse, HttpErrorResponse } from '@angular/common/http'; import { Observable, timer } from 'rxjs'; import { filter, finalize, takeUntil } from 'rxjs/operators'; import { SpinnerService } from './spinner.service'; @Injectable() export class InterceptorService implements HttpInterceptor { private activeRequests = 0; // Tracks ongoing HTTP requests constructor(private spinnerService: SpinnerService) {} private showSpinner() { this.spinnerService.show.next(true); console.log('Spinner shown'); } private hideSpinner() { this.spinnerService.show.next(false); console.log('Spinner hidden'); } intercept( req: HttpRequest<any>, next: HttpHandler, ): Observable<HttpEvent<any>> { // Skip spinner for specific requests (optional) if (req.url.includes('/assets/') || req.url.includes('/health-check')) { return next.handle(req); } // Increment active request count when a request starts this.activeRequests++; // Only attempt to show spinner if this is the first active request if (this.activeRequests === 1) { // Show spinner only if the request takes longer than 300ms (avoids flicker) timer(300) .pipe( // Cancel the timer if the request completes before 300ms takeUntil(next.handle(req).pipe( filter(e => e instanceof HttpResponse || e instanceof HttpErrorResponse) )) ) .subscribe(() => this.showSpinner()); } return next.handle(req).pipe( // Run this logic when the request completes (success OR failure) finalize(() => { // Decrement active request count this.activeRequests--; // Hide spinner only when all active requests are done if (this.activeRequests === 0) { this.hideSpinner(); } }) ); } }
Key Improvements Explained
Active Request Counter:
- The
activeRequestsvariable tracks how many HTTP requests are in flight. We only show the spinner when the first request starts (activeRequests === 1) and hide it when the last request finishes (activeRequests === 0).
- The
Flicker Prevention:
- The
timer(300)ensures the spinner only appears if a request takes longer than 300ms. If the request completes faster, the timer is canceled viatakeUntil, so the spinner never shows up.
- The
Error Handling:
- Using
finalizeguarantees we decrement the counter even if the request fails (e.g., network error), preventing the spinner from getting stuck in a visible state.
- Using
Optional Request Exclusion:
- The code includes a check to skip spinner logic for specific requests (like static assets or health checks) – adjust the URL patterns to match your needs.
Expected Behavior
After implementing this, you'll see:
- A single
Spinner shownlog if any request takes longer than 300ms - A single
Spinner hiddenlog once all requests have completed (success or failure)
内容的提问来源于stack exchange,提问作者viertel97
相关产品推荐
相关产品推荐

