如何用RxJS在后台更新Angular应用的缓存数据
解决方案:用RxJS流实现"先缓存后后台更新"逻辑
你需要重构数据获取逻辑,让Observable先发射缓存数据(如果存在),同时在后台发起API请求对比哈希,不匹配时更新缓存并触发通知。以下是具体实现步骤:
1. 重构数据获取方法,合并缓存与API流
修改countries服务中的方法,创建一个能同时处理缓存和API请求的Observable:
import { Observable, merge, of, Subject } from 'rxjs'; import { filter, tap, distinctUntilChanged } from 'rxjs/operators'; // 在服务中添加Subject用于通知数据更新 private dataUpdated$ = new Subject<boolean>(); public dataUpdated$ = this.dataUpdated$.asObservable(); public getCountriesWithCacheUpdate$(): Observable<IRawCountryData[]> { // 构建缓存数据流:仅当存在缓存时发射数据 const cachedData$ = of(this.cache.getCache<IRawCountryData[]>(this.localStorageKeys.countries)).pipe( filter(data => data !== null) ); // 构建API数据流:请求最新数据并处理缓存更新 const apiData$ = this.api.get$<IRawCountryData[]>(this.host).pipe( tap(apiData => { const cachedData = this.cache.getCache<IRawCountryData[]>(this.localStorageKeys.countries); if (!cachedData) { // 首次请求:直接缓存数据与哈希 this.cacheCountries(apiData); } else { // 对比哈希判断是否需要更新 if (!this.isCacheUpToDate(apiData)) { this.cacheCountries(apiData); // 触发数据更新通知 this.dataUpdated$.next(true); } } }) ); // 合并两个流:先发射缓存(如果有),再发射API数据 return merge(cachedData$, apiData$).pipe( // 避免重复发射相同数据 distinctUntilChanged((prev, curr) => this.isCacheUpToDate(curr)) ); }
2. 完善缓存与哈希对比逻辑
更新缓存方法和哈希校验逻辑,确保同时存储数据和对应的哈希值:
private cacheCountries(countries: IRawCountryData[]): void { const hash = this.calculateHash(countries); this.cache.setCache(this.localStorageKeys.countries, countries); this.cache.setCache(this.localStorageKeys.countriesHash, hash); } private isCacheUpToDate(newCountries: IRawCountryData[]): boolean { const cachedHash = this.cache.getCache<string>(this.localStorageKeys.countriesHash); if (!cachedHash) return false; const newHash = this.calculateHash(newCountries); return cachedHash === newHash; } // 简单哈希计算实现(实际项目可使用crypto-js等专业库) private calculateHash(data: any): string { const str = JSON.stringify(data); let hash = 0; for (let i = 0; i < str.length; i++) { hash = ((hash << 5) - hash) + str.charCodeAt(i); hash |= 0; // 转为32位整数 } return hash.toString(); }
3. 修改初始化逻辑,先展示缓存再监听更新
更新initGame方法,先展示缓存数据,同时订阅数据更新通知:
private initGame(): void { this.layout.showLoader(); this.countries .getCountriesWithCacheUpdate$() .pipe( takeUntilDestroyed(this.destroyRef), take(1), // 先取缓存数据展示 finalize(() => { this.layout.hideLoader(); }), map(rawCountries => this.countriesFilter.transform(rawCountries)), ) .subscribe(countries => { this.playService.setCountries(countries); this.playService.nextQuestion(); }); // 订阅数据更新通知,显示过期提示 this.countries.dataUpdated$.pipe( takeUntilDestroyed(this.destroyRef) ).subscribe(() => { // 替换为你的通知逻辑(如Toast、弹窗) this.notificationService.show('数据已更新,请刷新页面或重新开始游戏'); }); }
核心逻辑说明
- 缓存优先展示:通过
merge操作符先发射缓存数据,确保用户快速看到内容,不会因API请求延迟等待。 - 后台异步更新:API请求在后台执行,完全不阻塞UI,对比哈希后仅在数据变化时更新缓存并通知用户。
- 内存泄漏防护:全程使用
takeUntilDestroyed确保组件销毁时自动取消订阅,避免内存泄漏问题。
内容的提问来源于stack exchange,提问作者Denis
相关产品推荐
相关产品推荐

