如何用RxJS实现5分钟倒计时自动触发重载方法并重置计时
实现方案
问题原因
原代码存在两处核心错误导致无法达到预期效果:
takeWhile(x => x >= 0)会在倒计时归0后直接终止整个流,无法自动重启新一轮计时switchMap(() => this.reload())放在流上游,导致每秒都会触发一次reload方法,不符合5分钟触发一次的需求
完整实现代码
1. 组件属性定义
import { BehaviorSubject, interval, merge, Subject, timer } from 'rxjs'; import { startWith, switchMap, scan, takeWhile, takeUntil, tap } from 'rxjs/operators'; // 组件类内属性 // 倒计时剩余秒数,直接供模板订阅展示 countDown$ = new BehaviorSubject<number>(300); // 用于组件销毁时取消订阅,避免内存泄漏 private destroy$ = new Subject<void>(); // 手动触发重载的信号流 private manualReload$ = new Subject<void>();
2. ngOnInit 初始化逻辑
ngOnInit() { this.getUserDetails(); // 合并自动重载信号和手动重载信号 merge( // 自动重载:每5分钟(300秒)触发一次,startWith(0)表示组件初始化就触发第一次重载 interval(300 * 1000).pipe(startWith(0)), // 手动重载信号 this.manualReload$ ).pipe( // 任意重载信号触发时,先执行reload方法 tap(() => this.reload()), // 每次重载完成后启动新一轮5分钟倒计时 switchMap(() => timer(0, 1000).pipe( // 从299开始递减(触发重载的瞬间已经算进入下一轮倒计时) scan(acc => --acc, 299), takeWhile(x => x >= 0) )), takeUntil(this.destroy$) ).subscribe(remainingSecond => { // 更新倒计时数值供页面展示 this.countDown$.next(remainingSecond); }); }
3. 修改reload方法适配手动触发
// isManual参数区分是自动触发还是手动触发,避免重复触发重载循环 reload(isManual: boolean = false) { this.lastRefresh = new Date(); this.reloadDataService.reload(); // 手动触发时发射信号,重置倒计时 if (isManual) { this.manualReload$.next(); } }
4. 模板绑定示例
<!-- 倒计时展示,可自行格式化成分:秒格式 --> <span>距离下一次自动刷新还有 {{ countDown$ | async }} 秒</span> <!-- 手动触发按钮,传true表示是手动调用 --> <button (click)="reload(true)">手动刷新数据</button>
5. 组件销毁时清理订阅
ngOnDestroy() { this.destroy$.next(); this.destroy$.complete(); }
补充说明
如果不需要组件初始化就立刻触发第一次重载,只需要删除interval(300 * 1000)后面的.pipe(startWith(0))即可,第一轮重载会在组件初始化后5分钟触发。
内容的提问来源于stack exchange,提问作者user2004
相关产品推荐
相关产品推荐

