【发布时间】:2018-07-16 14:18:23
【问题描述】:
我想将HTTP-request 的结果缓存在一个类提供的Observable 中。此外,我必须能够显式地使缓存数据无效。因为在HttpClient 创建的Observable 上每次调用subscribe() 都会触发一个新请求,所以重新订阅似乎是我要走的路。所以我最终得到了以下服务:
import { Injectable } from '@angular/core'
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { shareReplay, first } from 'rxjs/operators';
@Injectable()
export class ServerDataService {
public constructor(
private http: HttpClient
) { }
// The request to retrieve all foos from the server
// Re-issued for each call to `subscribe()`
private readonly requestFoos = this.http.get<any[]>("/api/foo")
// Cached instances, may be subscribed to externally
readonly cachedFoos = this.requestFoos.pipe(shareReplay(1));
// Used for illustrating purposes, even though technically
// ngOnInit is not automatically called on Services. Just
// pretend this is actually called at least once ;)
ngOnInit() {
this.cachedFoos.subscribe(r => console.log("New cached foos"));
}
// Re-issues the HTTP request and therefore triggers a new
// item for `cachedFoos`
refreshFoos() {
this.requestFoos
.pipe(first())
.subscribe(r => {
console.log("Refreshed foos");
});
}
}
调用refreshFoos 时,我预计会发生以下情况:
- 发出了新的
HTTP-request,发生这种情况! -
"Refreshed foos"被打印出来,发生这种情况! -
"New cached foos"已打印,这不会发生!因此我的缓存未经过验证,并且使用async-pipe 订阅cachedFoos的 UI 未更新。
我知道,因为第 2 步有效,我可能会通过使用显式 ReplaySubject 并手动调用 next 而不是打印到控制台来组合手动解决方案。但这感觉很老套,我希望有更多“rxjsy 方式”来做到这一点。
这让我想到了两个密切相关的问题:
- 为什么在触发底层
requestFoos时,cachedFoos订阅没有更新? - 我怎样才能正确实现
refreshFoos-variant,最好只使用RxJS,更新cachedFoos的所有订阅者?
【问题讨论】:
-
你永远不会更新/更改
cachedFoos -
我在
ngOnInit订阅了它。在实际应用程序中,cachedFoos是通过async-pipeline 从HTML-模板订阅的,但 afaik 这应该与观察到的行为无关。 -
你订阅了它,但你永远不会改变它的价值(除非你没有发布那段代码)所以你永远不会得到控制台日志
-
您部分正确:在服务上使用
ngOnInit实际上并没有达到人们期望的效果(至少它不会自动调用),所以我的最小示例在这方面存在缺陷。但这不是真正的问题。 -
"每个对 cachedFoos 的订阅都必须使用 requestFoos 提供的确切实例" 这是正确的。我的评论可能模棱两可。
subscribe有点像说“给我一些数据”。该请求从 observable 传递到 observable,直到到达源,然后再传递给下一个操作员,直到到达订阅者。像shareReplay这样的操作符只通过一次订阅请求。所以在某种程度上,cachedFoos可以被视为一个来源,它本身持有对另一个来源的单一订阅。
标签: angular rxjs angular-httpclient