Rxjs实现定时请求并显示下次请求倒计时的技术问题
问题描述
开发一个支持按用户选择的x秒间隔自动更新的表格,请求依赖用户选择的筛选条件。核心要求:当用户选择10秒间隔并筛选某一天数据时,需在10秒后发起请求,必须等待响应完成后再重置定时器。
目前已实现定时请求逻辑,但无法显示下次请求的倒计时功能。现有服务端代码如下:
private dataIntervalSubject = new BehaviourSubject<number>(5); private dataLoadingSubject = new BehaviourSubject<boolean>(false); dataLoadingChanged$ = this.dataLoadingSubject.asObservable(); constructor( private globalFiltersService: GlobalFiltersService, private http: HttpClient ) { this.globalFiltersService.refreshIntervalSelectedAction$.subscribe(interval => { this.dataIntervalSubject.next(interval); }); } dataTimer$ = this.dataIntervalSubject.pipe( switchMap(val => interval(val * 1000)) ); data$ = combineLatest( this.dataTimer$, this.globalFiltersService.dataSelectedAction$ ).pipe( tap(() => this.dataLoadingSubject.next(true)), switchMap(([interval, date]) => this.http.get(`dataUrl/${date}`) .pipe( tap(() => this.dataLoadingSubject.next(false)) ) ) );
组件中使用async pipe的变量:
dataLoading$ = this.dataService.dataLoadingChanged$; dataRequestTimeRemaining$ = this.dataService.dataTimer$; data$ = this.dataService.data$;
当前dataRequestTimeRemaining$仅统计请求次数,无法实现倒计时,需调整代码实现该功能。
解决方案
核心思路是:创建一个可重置的倒计时流,在用户修改间隔、请求完成这两个时机重置倒计时;同时用倒计时结束的信号触发数据请求,确保请求完成后才重新开始倒计时。
步骤1:修改服务类代码
import { BehaviourSubject, Subject, interval, combineLatest } from 'rxjs'; import { switchMap, tap, map, takeWhile, last, repeat } from 'rxjs/operators'; private dataIntervalSubject = new BehaviourSubject<number>(5); private dataLoadingSubject = new BehaviourSubject<boolean>(false); private countdownReset$ = new Subject<void>(); // 用于触发倒计时重置 dataLoadingChanged$ = this.dataLoadingSubject.asObservable(); dataRequestTimeRemaining$ = this.createCountdownStream(); // 倒计时流 constructor( private globalFiltersService: GlobalFiltersService, private http: HttpClient ) { // 间隔变化时重置倒计时 this.globalFiltersService.refreshIntervalSelectedAction$.subscribe(interval => { this.dataIntervalSubject.next(interval); this.countdownReset$.next(); }); } // 创建倒计时流:从当前间隔值开始每秒递减,直到0 private createCountdownStream() { return combineLatest([this.dataIntervalSubject, this.countdownReset$]).pipe( switchMap(([interval]) => { // 生成从interval到0的倒计时序列 return interval(1000).pipe( map(i => interval - i), takeWhile(remaining => remaining >= 0), repeat() // 倒计时到0后,等待下一次重置信号 ); }) ); } // 修改数据请求流:用倒计时结束信号触发请求,请求完成后重置倒计时 data$ = combineLatest([ this.dataRequestTimeRemaining$.pipe(last(remaining => remaining === 0)), // 倒计时到0时触发 this.globalFiltersService.dataSelectedAction$ ]).pipe( tap(() => this.dataLoadingSubject.next(true)), switchMap(([_, date]) => this.http.get(`dataUrl/${date}`).pipe( tap(() => { this.dataLoadingSubject.next(false); this.countdownReset$.next(); // 请求完成后重置倒计时 }) )), repeat() // 请求完成后,等待下一次倒计时结束信号 );
步骤2:组件中使用倒计时流
保持组件代码不变,dataRequestTimeRemaining$现在会每秒输出剩余秒数,直接用async pipe绑定到模板即可:
<p>下次请求剩余:{{ dataRequestTimeRemaining$ | async }} 秒</p>
关键逻辑说明
countdownReset$作为重置触发器,在间隔修改、请求完成时发出信号,确保倒计时从新的间隔值重新开始createCountdownStream()生成的流每秒更新剩余秒数,直到0,然后等待下一次重置信号data$通过监听倒计时到0的信号发起请求,请求完成后立即重置倒计时,严格符合"请求完成后再重置定时器"的要求
内容的提问来源于stack exchange,提问作者DeejC
相关产品推荐
相关产品推荐

