Angular RxJs 如何等待Observable的finalize执行 解决Ionic加载动画残留问题
问题本质
LoadingController的create、present方法均为异步Promise逻辑,原代码中请求发起和loading创建是并行执行的,当请求响应速度快于loading创建速度时,finalize中的销毁逻辑会先执行,此时loading还未完成present,销毁逻辑不生效,后续loading创建完成后就会一直显示无法关闭。
修复思路
- 使用RxJS的
from方法将LoadingController的Promise异步操作转为Observable,确保loading创建/确认无需创建的逻辑执行完成后,再发起HTTP请求,从时序上保证finalize销毁逻辑一定晚于loading创建逻辑 - 新增请求计数变量处理并发请求场景,避免多个请求并行时,第一个请求结束就关闭了其他仍在运行的请求对应的loading
- 修正原finalize中无效的
delay(500)写法,delay是RxJS操作符,无法直接在普通回调函数中调用
修复后完整代码
import { from } from 'rxjs'; import { switchMap } from 'rxjs/operators'; export class HttpRequestInterceptor implements HttpInterceptor { // 并发请求计数 private requestCount = 0; constructor( private loadingCtrl: LoadingController, private alertCtrl: AlertController, private authService: AuthService) { } intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { this.requestCount++; // 先处理loading创建逻辑,完成后再发起请求 return from(this.handleLoadingPresent()).pipe( switchMap(() => next.handle(request).pipe( catchError(err => { if (err instanceof HttpErrorResponse) { console.log('error', err); // 出错时先判断计数再销毁loading this.handleLoadingDismiss(); switch ((<HttpErrorResponse>err).status) { // 登录态失效 case 401: this.PresentFailedAlert(err.error['message']); return EMPTY; case 409: this.PresentFailedAlert(err.error['message']); return EMPTY; default: return throwError(err); } } else { return throwError(err); } }), retryWhen(err => { let retries = 1; return err.pipe( delay(3000), map(error => { if (retries++ === 3) { throw error; } return error; }) ) }), catchError(err => { console.log(err); this.PresentFailedAlert(err.error['message']); return EMPTY; }), finalize(() => { this.handleLoadingDismiss(); }) )) ); } // 封装loading展示逻辑 private async handleLoadingPresent() { const hasLoading = await this.loadingCtrl.getTop(); // 只有第一个请求需要创建loading if (!hasLoading && this.requestCount === 1) { const loading = await this.loadingCtrl.create({ spinner: 'circular', translucent: true }); await loading.present(); } } // 封装loading销毁逻辑 private async handleLoadingDismiss() { this.requestCount--; // 所有请求都结束才销毁loading if (this.requestCount === 0) { const hasLoading = await this.loadingCtrl.getTop(); if (hasLoading) { await this.loadingCtrl.dismiss(); } } } // 原有的提示方法保留即可 private PresentFailedAlert(msg: string) { // 你的原有实现 } }
注意事项
- 该实现从时序上保证了loading创建完成后才会发起请求,不会再出现销毁逻辑早于创建逻辑执行的问题
- 新增的计数逻辑适配并发请求场景,避免多个并行请求时loading提前关闭的问题
- 所有异步操作都使用async/await同步化处理,避免时序混乱
内容的提问来源于stack exchange,提问作者user2570937
相关产品推荐
相关产品推荐

