我想缓存和终止来自例如带有某些参数的GET请求。
用例示例:
假设我建立这样的服务:
@Injectable()
export class ProductService {
constructor(private http: HttpClient) {}
getProduct$(id: string): Observable<Product> {
return this.http.get<Product>(`${API_URL}/${id}`);
}
getProductA$(id: string): Observable<ProductA> {
return this.getProduct$(id).pipe(
// bunch of complicated operations here
};
}
getProductB$(id: string): Observable<ProductB> {
return this.getProduct$(id).pipe(
// bunch of other complicated operations here
};
}
}
现在无论出于何种原因,在组件A中调用函数A,在组件B中调用函数B。我知道这可以通过另一种方式完成(例如,顶级智能组件获取HTTP数据并将其传递给输入参数),但是无论出于何种原因,这两个组件都是“智能”的,它们各自调用服务函数。
两个组件都加载在同一页面上,因此发生了两个预订=向同一端点的两个HTTP请求-尽管我们知道结果很可能是相同的。
我只想缓存getProduct$
的响应,但是我也希望该缓存很快过期,因为产品管理部门的玛格丽特(Margareth)将在2分钟内更改产品价格。
我尝试过但不起作用的内容:
基本上,我尝试使用窗口时间为5s的shareReplay保留热门可观察词的字典。我的假设是,如果(源)可观察对象完成或订阅数为0,则下一个订阅者将简单地激发该可观察对象,但事实并非如此。
private product$: { [productId: string]: Observable<Product> } = {};
getProduct$(id: string): Observable<Product> {
if (this.product$[id]) {
return this.product$[id];
}
this.product$[id] = this.http.get<Product>(`${API_URL}/${id}`)
.pipe(
shareReplay(1, 5000), // expire after 5s
)
);
return this.product$[id];
}
我想,我可以尝试使用finalize或finally在完成后从字典中删除可观察的对象,但不幸的是,每个取消订阅的对象也都会调用这些对象。
因此解决方案可能必须更复杂。
有什么建议吗?
答案 0 :(得分:1)
如果我对您的理解正确,那么您希望根据其id
参数来缓存响应,因此当我用不同的getProduct()
来创建两个id
时,我将得到两个不同的未缓存结果。 / p>
我认为最后一个变体几乎是正确的。您希望它退订其父级,以便以后可以重新订阅并刷新缓存的值。
如果我正确地记得shareReplay
发生了更改并且没有重新订阅其来源,那么shareReplay
运算符在RxJS 5.5之前的工作方式有所不同。后来在RxJS 6.4中重新实现了它,您可以在其中基于传递给shareReplay
的配置对象来修改其行为。由于您使用的是shareReplay(1, 5000)
,似乎您使用的是RxJS <6.4,因此最好使用publishReplay()
和refCount()
运算符。
private cache: Observable<Product>[] = {}
getProduct$(id: string): Observable<Product> {
if (!this.cache[id]) {
this.cache[id] = this.http.get<Product>(`${API_URL}/${id}`).pipe(
publishReplay(1, 5000),
refCount(),
take(1),
);
}
return this.cache[id];
}
请注意,我还包括了take(1)
。这是因为我希望链在publishReplay
发出缓冲区之后并在订阅其源Observable之前立即完成。不必订阅其源,因为我们只想使用缓存的值。 5秒后,缓存的值将被丢弃,publishReplay
将再次订阅其源。
我希望这一切都说得通:)。