无法缓存多服务复用请求且无法取消重复点击请求的技术求助
解决方案:请求缓存与重复点击取消实现
核心思路
用一个专门的数据缓存服务(仅这一个服务使用RxJS),负责:
- 管理每个接口的请求状态与缓存结果
- 通过
switchMap自动取消前一次pending请求 - 对外暴露Promise风格的API,组件和其他服务无需接触RxJS
实现步骤
1. 实现数据缓存服务
import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable, Subject, switchMap, shareReplay, take } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class DataCacheService { private pendingRequests = new Map<string, Observable<any>>(); private cachedResponses = new Map<string, any>(); private requestTrigger = new Subject<string>(); constructor(private http: HttpClient) { // 统一处理请求触发流,switchMap自动取消前一次未完成请求 this.requestTrigger.pipe( switchMap((key) => this.fetchAndCache(key)) ).subscribe(); } // 对外暴露的Promise方法,供组件/其他服务调用 async fetchData(url: string): Promise<any> { // 优先返回缓存结果 if (this.cachedResponses.has(url)) { return this.cachedResponses.get(url); } // 触发请求,返回Promise this.requestTrigger.next(url); return this.pendingRequests.get(url)?.pipe(take(1)).toPromise(); } private fetchAndCache(url: string): Observable<any> { // 已有pending请求时直接复用 if (this.pendingRequests.has(url)) { return this.pendingRequests.get(url)!; } // 发起新请求并缓存 const request$ = this.http.get(url).pipe( shareReplay(1), // 缓存结果供后续订阅复用 take(1), { next: (res) => { this.cachedResponses.set(url, res); this.pendingRequests.delete(url); }, error: () => { this.pendingRequests.delete(url); } } ); this.pendingRequests.set(url, request$); return request$; } }
2. 业务服务组合多接口请求
以UserDetails为例,创建业务服务组合多个接口数据,对外返回Promise:
import { Injectable } from '@angular/core'; import { DataCacheService } from './data-cache.service'; @Injectable({ providedIn: 'root' }) export class UserService { constructor(private dataCache: DataCacheService) {} async getUserDetails(userId: string): Promise<{ user: any; signs: any }> { const [user, signs] = await Promise.all([ this.dataCache.fetchData(`/api/users/${userId}`), this.dataCache.fetchData(`/api/users/${userId}/signs`) ]); return { user, signs }; } }
3. 组件调用(完全无RxJS)
组件直接调用业务服务的Promise方法,处理加载状态与结果:
import { Component } from '@angular/core'; import { UserService } from './services/user.service'; @Component({ selector: 'app-root', template: ` <button (click)="loadUser()" [disabled]="loading">加载用户数据</button> <div *ngIf="userDetails"> <h3>{{ userDetails.user.name }}</h3> <p>签名:{{ userDetails.signs.content }}</p> </div> ` }) export class AppComponent { userDetails: any; loading = false; constructor(private userService: UserService) {} async loadUser() { this.loading = true; try { this.userDetails = await this.userService.getUserDetails('123'); } catch (err) { console.error(err); } finally { this.loading = false; } } }
关键特性说明
- 重复请求拦截:同一接口存在pending请求或已缓存结果时,直接复用,避免重复调用后端
- 重复点击取消:通过单
requestTrigger流统一处理请求,switchMap自动取消前一次未完成请求 - 低RxJS侵入:仅数据缓存服务使用RxJS,组件和业务服务完全基于Promise,符合项目限制
- 数据共享:多个服务调用同一接口时,共享缓存结果,无需重复请求
解决你之前的问题
之前出现请求叠加,是因为每次点击都创建了新的Observable并订阅。现在通过单流统一处理所有请求触发,switchMap会自动终止前一次pending请求,不会产生叠加。
内容的提问来源于stack exchange,提问作者IDK4real
相关产品推荐
相关产品推荐

