RxJS问题:SpinnerService最后一次设true后20秒自动重置为false
解决SpinnerService自动重置加载状态的问题
问题背景
现有Angular加载状态服务SpinnerService,用于控制全局加载动画,需求是:当最后一次调用showLoader()(即isLoading$设为true)后,若20秒内无新的true状态设置,自动将isLoading$重置为false。
原尝试代码仅能触发第一次true状态的超时,后续新的true无法重置计时器,原因是timeout操作符在第一次超时后会直接终止订阅,且未处理新事件到来时重置计时器的逻辑。
正确实现方案
利用switchMap+timer组合实现"每次收到true就重启20秒计时器"的逻辑,同时添加内存泄漏防护:
import { Injectable, OnDestroy } from '@angular/core'; import { BehaviorSubject, Observable, timer, Subject } from 'rxjs'; import { filter, switchMap, takeUntil, throttleTime, shareReplay } from 'rxjs/operators'; @Injectable({ providedIn: 'root' }) export class SpinnerService implements OnDestroy { public throttleTime: number = 10; public isLoading$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false); private destroy$ = new Subject<void>(); constructor() { this.setupAutoHideTimer(); } public showLoader(): void { this.isLoading$.next(true); } public hideLoader(): void { this.isLoading$.next(false); } public get isLoadingAPIVal$(): Observable<boolean> { return this.isLoading$.pipe( throttleTime(this.throttleTime), shareReplay() ); } private setupAutoHideTimer(): void { this.isLoading$ .pipe( // 只关注true状态的变更 filter(val => val), // 每次收到true,就启动一个20秒的定时器;新的true会取消之前的定时器并重启 switchMap(() => timer(20000)), // 服务销毁时终止订阅,避免内存泄漏 takeUntil(this.destroy$) ) .subscribe(() => { // 超时后重置为false this.isLoading$.next(false); }); } ngOnDestroy(): void { this.destroy$.next(); this.destroy$.complete(); this.isLoading$.complete(); } }
关键逻辑解释
filter(val => val):仅处理isLoading$变为true的事件,忽略false状态switchMap(() => timer(20000)):核心逻辑,每次收到true时创建20秒定时器流;若定时器触发前收到新true,switchMap会取消旧定时器并重启新的,实现计时器重置takeUntil(this.destroy$):服务销毁时自动取消订阅,防止内存泄漏- 抽离
setupAutoHideTimer方法,让代码结构更清晰
内容的提问来源于stack exchange,提问作者byCoder
相关产品推荐
相关产品推荐

