Angular中HttpInterceptor如何检测已取消的XHR请求以隐藏加载指示器?
在Angular HttpInterceptor中检测已取消的请求
当然可以解决这个问题!当Chrome取消过于频繁的API调用时,Angular的HttpClient会抛出特定的取消错误,我们可以在HttpInterceptor里捕获这个状态,同时利用RxJS的操作符确保加载指示器总能被正确隐藏。
核心思路
Angular中请求被取消时,会抛出CanceledError(需要从@angular/common/http导入)。另外,RxJS的finalize操作符是关键——它会在Observable完成、出错或者被取消时执行,完美适配我们需要清理加载状态的场景。
完整代码示例
假设你有一个LoadingService来管理加载指示器的显示/隐藏(用计数方式处理多并发请求更可靠),下面是改造后的拦截器:
import { Injectable } from '@angular/core'; import { HttpRequest, HttpHandler, HttpInterceptor, HttpErrorResponse, HttpResponse } from '@angular/common/http'; import { finalize, catchError, tap } from 'rxjs/operators'; import { throwError } from 'rxjs'; import { CanceledError } from '@angular/common/http'; import { LoadingService } from './loading.service'; // 你的加载服务 @Injectable() export class GlobalHttpInterceptor implements HttpInterceptor { constructor(private loadingService: LoadingService) {} intercept(request: HttpRequest<unknown>, next: HttpHandler) { // 增加请求计数 this.loadingService.incrementRequestCount(); // 500ms后如果请求仍未完成,显示加载指示器 const showLoadingTimer = setTimeout(() => { if (this.loadingService.getRequestCount() > 0) { this.loadingService.showLoading(); } }, 500); return next.handle(request).pipe( // 正常响应完成时的处理 tap(event => { if (event instanceof HttpResponse) { // 这里可以做成功响应的额外处理 } }), // 捕获错误,包括取消请求的情况 catchError((error: HttpErrorResponse) => { if (error instanceof CanceledError) { console.log(`请求已取消: ${request.url}`); } else { console.error(`请求出错: ${request.url}`, error); } return throwError(() => error); }), // 无论请求成功、失败还是取消,最终都会执行这里的逻辑 finalize(() => { clearTimeout(showLoadingTimer); // 减少请求计数 this.loadingService.decrementRequestCount(); // 当计数为0时,隐藏加载指示器 if (this.loadingService.getRequestCount() === 0) { this.loadingService.hideLoading(); } }) ); } }
关键细节说明
- 用计数管理加载状态:避免单个请求取消导致加载指示器提前隐藏,只有当所有请求都完成(包括取消)时才隐藏。
finalize的可靠性:不管请求是正常完成、抛出错误还是被取消,finalize都会执行,确保我们总能清理定时器并更新加载状态。CanceledError检测:如果你需要对取消的请求做特殊处理(比如日志上报),可以通过instanceof CanceledError精准识别。
配套的LoadingService示例(参考)
为了让拦截器正常工作,你的加载服务可以这样实现:
import { Injectable } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class LoadingService { private requestCount = 0; private isLoadingSubject = new BehaviorSubject<boolean>(false); public isLoading$ = this.isLoadingSubject.asObservable(); incrementRequestCount() { this.requestCount++; } decrementRequestCount() { this.requestCount = Math.max(0, this.requestCount - 1); this.updateLoadingState(); } getRequestCount() { return this.requestCount; } private updateLoadingState() { this.isLoadingSubject.next(this.requestCount > 0); } }
这样改造后,不管请求是正常完成、出错还是被Chrome取消,加载指示器都能被正确隐藏啦!
内容的提问来源于stack exchange,提问作者Justin Lutsky
相关产品推荐
相关产品推荐

