我发现了许多缓存响应式可观察对象的方法,更具体地说,是超文本传输协议请求的结果。然而,我对提议的解决方案并不完全满意,原因如下:
1.这个答案https://stackoverflow.com/a/36417240/1063354使用私有字段来存储第一次请求的结果,并在之后的所有调用中重复使用。
代码:
private data: Data;
getData() {
if(this.data) {
return Observable.of(this.data);
} else {
...
}
}
可悲的是,可观察对象的力量被完全忽略了——你手动做所有的事情。事实上,如果我满足于将结果分配给局部变量/字段,我不会寻找合适的解决方案。另一个我认为是糟糕做法的重要事情是服务不应该有状态——也就是说,不应该有包含从一个调用到另一个调用更改的数据的私有字段。清除缓存相当容易——只需将this.data设置为空,请求就会重新执行。
2.本答复https://stackoverflow.com/a/36413003/1063354建议使用ReplaySub:
private dataObs$ = new ReplaySubject(1);
constructor(private http: Http) { }
getData(forceRefresh?: boolean) {
// If the Subject was NOT subscribed before OR if forceRefresh is requested
if (!this.dataObs$.observers.length || forceRefresh) {
this.http.get('http://jsonplaceholder.typicode.com/posts/2').subscribe(
data => this.dataObs$.next(data),
error => {
this.dataObs$.error(error);
// Recreate the Observable as after Error we cannot emit data anymore
this.dataObs$ = new ReplaySubject(1);
}
);
}
return this.dataObs$;
}
看起来非常棒(再次-清除缓存没有问题),但我无法映射此调用的结果,即。
service.getData().map(data => anotherService.processData(data))
这是因为底层观察者没有调用它的完整方法。我很确定很多反应式方法在这里也不起作用。要实际获取数据,我必须订阅这个可观察对象,但我不想这样做:我想通过解析器获取我的一个组件的缓存数据,解析器应该返回一个可观察对象(或promise),而不是订阅:
路线
{
path: 'some-path',
component: SomeComponent,
resolve: {
defaultData: DefaultDataResolver
}
}
解决者
...
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Data> {
return this.service.getData();
}
该组件永远不会被激活,因为它的依赖关系永远不会被解析。
3.在这里https://stackoverflow.com/a/36296015/1063354我发现了一个使用发布最后(). refCount()的提议。
代码:
getCustomer() {
return this.http.get('/someUrl')
.map(res => res.json()).publishLast().refCount();
}
这满足了我对缓存和解析的要求,但我还没有找到一个干净整洁的解决方案来清除缓存。
我错过了什么吗?有人能想出更好的方法来缓存反应性可观察对象,能够映射它们的结果,并在缓存的数据不再相关时刷新它吗?
这个简单的类缓存结果,因此您可以多次订阅. value并只发出1个请求。您还可以使用.重新加载()来发出新请求并发布数据。
您可以像这样使用它:
let res = new RestResource(() => this.http.get('inline.bundleo.js'));
res.status.subscribe((loading)=>{
console.log('STATUS=',loading);
});
res.value.subscribe((value) => {
console.log('VALUE=', value);
});
和来源:
export class RestResource {
static readonly LOADING: string = 'RestResource_Loading';
static readonly ERROR: string = 'RestResource_Error';
static readonly IDLE: string = 'RestResource_Idle';
public value: Observable<any>;
public status: Observable<string>;
private loadStatus: Observer<any>;
private reloader: Observable<any>;
private reloadTrigger: Observer<any>;
constructor(requestObservableFn: () => Observable<any>) {
this.status = Observable.create((o) => {
this.loadStatus = o;
});
this.reloader = Observable.create((o: Observer<any>) => {
this.reloadTrigger = o;
});
this.value = this.reloader.startWith(null).switchMap(() => {
if (this.loadStatus) {
this.loadStatus.next(RestResource.LOADING);
}
return requestObservableFn()
.map((res) => {
if (this.loadStatus) {
this.loadStatus.next(RestResource.IDLE);
}
return res;
}).catch((err)=>{
if (this.loadStatus) {
this.loadStatus.next(RestResource.ERROR);
}
return Observable.of(null);
});
}).publishReplay(1).refCount();
}
reload() {
this.reloadTrigger.next(null);
}
}
使用选项3。为了允许清除缓存,您可以将可观察对象分配给私有成员并返回它,例如。
getCustomer() {
if (!this._customers) {
this._customers = this.http.get('/someUrl')
.map(res => res.json()).publishLast().refCount();
}
return this._customers
}
clearCustomerCache() {
this._customers = null;
}
我的缓存方法是在减速器/扫描fn中保持状态:
编辑3:添加了一段代码以通过键盘事件使缓存无效。
编辑2:window
运算符也适用于任务,并允许以非常简洁的方式表达逻辑:
const Rx = require('rxjs/Rx');
const process = require('process');
const stdin = process.stdin;
// ceremony to have keypress events in node
stdin.setRawMode(true);
stdin.setEncoding('utf8');
stdin.resume();
// split the keypress observable into ctrl-c and c observables.
const keyPressed$ = Rx.Observable.fromEvent(stdin, 'data').share();
const ctrlCPressed$ = keyPressed$.filter(code => code === '\u0003');
const cPressed$ = keyPressed$.filter(code => code === 'c');
ctrlCPressed$.subscribe(() => process.exit());
function asyncOp() {
return Promise.resolve(Date().toString());
}
const invalidateCache$ = Rx.Observable.interval(5000).merge(cPressed$);
const interval$ = Rx.Observable.interval(1000);
interval$
.windowWhen(() => invalidateCache$)
.map(win => win.mergeScan((acc, value) => {
if (acc === undefined) {
const promise = asyncOp();
return Rx.Observable.from(promise);
}
return Rx.Observable.of(acc);
}, undefined))
.mergeAll()
.subscribe(console.log);
它将仅每5s执行一次异步选项,并缓存结果以防可观察对象上的其他遗漏。
Sun Apr 16 2017 11:24:53 GMT+0200 (CEST)
Sun Apr 16 2017 11:24:53 GMT+0200 (CEST)
Sun Apr 16 2017 11:24:53 GMT+0200 (CEST)
Sun Apr 16 2017 11:24:53 GMT+0200 (CEST)
Sun Apr 16 2017 11:24:57 GMT+0200 (CEST)
Sun Apr 16 2017 11:24:57 GMT+0200 (CEST)