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

Angular借助RxJS实现Store定时重取数据仅首次生效问题

问题根源
  • 订阅逻辑位置错误:你将reloadLeaderboards$的订阅逻辑放在了updateLeaderBoard方法中,每次调用该方法都会新建一个订阅,但从未主动给reloadLeaderboards$推送新的事件,除了首次可能的默认触发外,后续不会执行switchMap内的请求逻辑。
  • 重复调用初始化逻辑:组件定时器每次都调用store.init(),而init方法只需要执行一次初始化操作,重复调用只会创建多余的无用订阅,还会造成内存泄漏。
  • 事件触发逻辑缺失:你没有实现主动推送刷新事件的逻辑,只是重复创建订阅,自然无法触发新的接口请求。
修复方案

1. 调整Store代码

将订阅逻辑移到构造函数中仅初始化一次,新增主动触发刷新的方法:

@Injectable()
export class LeaderboardStore extends Store<LeaderboardStoreState> implements OnDestroy {
    private storeRequestStateUpdater: StoreRequestStateUpdater;
    private ngUnsubscribe$: Subject<undefined> = new Subject();
    private reloadLeaderboards$: Subject<CompetitionFilteringOptions> = new Subject();

    constructor(private endpoint: LeaderboardEndpoint) {
        super(new LeaderboardStoreState());
        this.storeRequestStateUpdater = endpointHelpers.getStoreRequestStateUpdater(this);
        // 订阅逻辑仅初始化一次
        this.initReloadListener();
    }

    private initReloadListener(): void {
        this.reloadLeaderboards$
            .pipe(
                switchMap((filterBy: CompetitionFilteringOptions) => {
                    console.log('switch maps')
                    return this.endpoint.listLeaderboards(this.storeRequestStateUpdater, filterBy);
                }),
                tap((leaderboards: any) => {
                    // 建议先深拷贝返回数据再修改,避免引用类型导致的缓存问题
                    const clonedLeaderboards = JSON.parse(JSON.stringify(leaderboards));
                    const currentUser = clonedLeaderboards[0].entries.find(entry => entry.userName === 'legenden420');
                    currentUser.me = true;
                    const currentUserPoints = currentUser.points;
                    this.setState({
                        ...this.state,
                        leaderboards: clonedLeaderboards,
                        currentUserPoints
                    });
                }),
                retry(),
                takeUntil(this.ngUnsubscribe$)
            )
            .subscribe();
    }

    // 对外暴露的刷新触发方法
    triggerRefresh(filterBy: CompetitionFilteringOptions): void {
        console.log('reloaded leaderboard');
        this.reloadLeaderboards$.next(filterBy);
    }

    // 首次初始化调用,可保留
    init(initialFilter: CompetitionFilteringOptions): void {
        this.triggerRefresh(initialFilter);
    }

    ngOnDestroy(): void {
        this.ngUnsubscribe$.next();
        this.ngUnsubscribe$.complete();
    }
}

2. 调整组件代码

定时器仅调用刷新方法,init只在组件初始化时执行一次:

ngOnInit() {
    // 替换为你实际使用的初始过滤参数
    const filterOptions = {} as CompetitionFilteringOptions;
    // 首次加载只调用一次
    this.store.init(filterOptions);
    // 定时器实现定时刷新
    this.timer$ = interval(5000);
    console.log('init timer');
    this.subscription = this.timer$.subscribe(t => {
        console.log('updated nr ' + t);
        // 传入当前使用的过滤参数触发刷新
        this.store.triggerRefresh(filterOptions);
    });
}

// 组件销毁时清理订阅,避免内存泄漏
ngOnDestroy(): void {
    this.subscription?.unsubscribe();
}
额外排查点

如果修改后仍然返回旧数据,按以下顺序排查:

  • 打开浏览器Network面板,确认每次触发刷新是否真的发出了新的HTTP请求,响应内容是否为最新数据
  • 如果请求未发出,检查listLeaderboards方法是否做了本地缓存,没有真实发起请求
  • 如果请求已发出但响应是旧数据,可在请求头添加Cache-Control: no-cache禁用浏览器缓存,或者给请求URL添加时间戳参数避免缓存
  • 如果响应是新数据但状态未更新,检查setState方法是否正确触发了状态变更通知

内容的提问来源于stack exchange,提问作者Kristoffer Tølbøll

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 05:24:03