我很难找到我开始构建的服务序列。我想要做的是从基于Promise的存储提供程序加载缓存,然后启动具有该缓存的observable。一旦HTTP请求结算,我就想用旧响应替换旧缓存。
我很难搞清楚这个过程,并希望得到任何帮助。
伪代码
import {Injectable} from "@angular/core";
import {Http} from "@angular/http";
import rxjs/add/operators/map;
import rxjs/add/operators/startWith;
import {Storage} from "@ionic/storage";
@Injectable()
export class DataService {
private _cache = {};
constructor(public http: Http; public storage: Storage){}
public getCache(): Promise<any> {
return this.storage.get('storageKey');
}
public setCache(val: any): Promise<any> {
return this.storage.set('storageKey', val);
}
public getData(): Observable<any> {
// Step 1: Get cache.
this.getCache().then(cache => this._cache = cache);
// Step 2: Make HTTP request.
return this.http.get('endpoint')
// Step 3: Set data mapping
.map(res => this.normalize(res))
// Step 4: Use resolved cache as startWith value
.startWith(this._cache)
// Step 5: Set cache to response body.
.do(result => this.setCache(result).then(() => console.log('I resolved'));
}
private normalize(data: Response): any {
// Fun data-mapping stuff here
}
}
此代码旨在在移动设备上运行,其中速度幻觉和离线功能是第一优先级。通过最初使用旧缓存设置视图,用户可以查看已存储的内容,如果HTTP请求失败(网络速度慢或没有网络),没什么大不了的,我们会回退到缓存。但是,如果用户在线,我们应该利用这个机会用来自HTTP请求的新响应替换旧缓存。
感谢您的帮助,如果我需要进一步详细说明,请告诉我。
答案 0 :(得分:2)
如果我已正确理解要求,
修改 - 调整后的讨论
getData() {
// storage is the cache
const cached$ = Observable.fromPromise(this.storage.get('data'))
// return an observable with first emit of cached value
// and second emit of fetched value.
// Use concat operator instead of merge operator to ensure cached value is emitted first,
// although expect storage to be faster than http
return cached$.concat(
this.http.get('endpoint')
.map(res => res.json())
.do(result => this.storage.set('data', result))
);
}
编辑#2 - 处理最初为空的缓存
在CodePen here中测试后,我意识到有一个边缘情况,这意味着我们应该使用merge()
代替concat()
。如果存储空间最初为空,concat
将会阻止,因为cached$
永远不会发出。
此外,需要添加distinctUntilChanged()
,因为如果存储空间最初为空,merge
将返回第一次提取两次。
您可以通过注释掉在CodePen中设置'prev value'
的行来查看边缘大小写。
/* Solution */
const getData = () => {
const cached$ = Observable.fromPromise(storage.get('data'))
return cached$
.merge(
http.get('endpoint')
.map(res => res.json())
.do(result => storage.set('data', result))
)
.distinctUntilChanged()
}
/* Test solution */
storage.set('data', 'prev value') // initial cache value
setTimeout( () => { getData().subscribe(value => console.log('call1', value)) }, 200)
setTimeout( () => { getData().subscribe(value => console.log('call2', value)) }, 300)
setTimeout( () => { getData().subscribe(value => console.log('call3', value)) }, 400)
答案 1 :(得分:0)
使用concat确保订单?
const cache = Rx.Observable.of('cache value');
const http = function(){
return Rx.Observable.of('http value');
};
var storage={}
storage.set=function(value){
return Rx.Observable.of('set value: '+value);
}
const example = cache.concat(http().flatMap(storage.set));
const subscribe = example.subscribe(val => console.log('sub:',val));`
答案 2 :(得分:0)
取自 Richard Matsen ,这是我来到的最终结果。我添加了一个catch
子句,这可能有点多余,但在网络上使用Ionic,你会得到他们的调试屏幕,这就是为什么它在那里。可以catch
进行改进,以便do
无法运行吗?
public getFeed(source: SocialSource): Observable<any> {
const cached$ = Observable.fromPromise(this._storage.get(`${StorageKeys.Cache.SocialFeeds}.${source}`));
const fetch$ = this._http.get(`${Configuration.ENDPOINT_SOCIAL}${source}`)
.map(res => this.normalizeFacebook(res))
.catch(e => cached$)
.do(result => this._storage.set(`${StorageKeys.Cache.SocialFeeds}.${source}`, result));
return cached$.concat(fetch$);
}