You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

实现无闪烁延迟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

  1. Active Request Counter:

    • The activeRequests variable 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).
  2. 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 via takeUntil, so the spinner never shows up.
  3. Error Handling:

    • Using finalize guarantees we decrement the counter even if the request fails (e.g., network error), preventing the spinner from getting stuck in a visible state.
  4. 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 shown log if any request takes longer than 300ms
  • A single Spinner hidden log once all requests have completed (success or failure)

内容的提问来源于stack exchange,提问作者viertel97

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 09:00:14