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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 21:57:04