首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >角2缓存http请求使用可观测数据的威力

角2缓存http请求使用可观测数据的威力
EN

Stack Overflow用户
提问于 2017-04-13 08:53:12
回答 3查看 5.7K关注 0票数 5

我已经找到了许多方法来缓存反应性的可观察性,更具体地说,找到了http请求的结果。不过,由于以下原因,我对建议的解决办法并不完全满意:

1.这个答案https://stackoverflow.com/a/36417240/1063354使用一个私有字段来存储第一个请求的结果,并在随后的所有调用中重用它。

守则:

代码语言:javascript
复制
private data: Data;    
getData() {
    if(this.data) {
        return Observable.of(this.data);
    } else {
        ...
    }
}

可悲的是,可观察性的力量被完全忽略了--所有的事情都是你手工完成的。事实上,如果我满足于将结果分配给局部变量/字段,我就不会寻找合适的解决方案。另一件我认为不好的做法是,服务不应该有状态--也就是说,不应该有包含数据的私有字段,这些数据在调用之间发生了变化。很容易清除缓存--只要将this.data设置为null,请求就会被重新执行。

2. https://stackoverflow.com/a/36413003/1063354建议使用ReplaySubject:

代码语言:javascript
复制
    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$;
    }

看起来非常棒(清除缓存也没有问题),但是我无法映射这个调用的结果,即

代码语言:javascript
复制
service.getData().map(data => anotherService.processData(data))

这是因为底层观察者没有调用它的完整方法。我很肯定很多反应的方法在这里也不起作用。要真正获得数据,我必须订阅这个可观察到的数据,但我不想这样做:我希望通过解析器获得我的组件的缓存数据,该解析器应该返回一个可观察到的Subscription (或承诺),而不是一个

路由

代码语言:javascript
复制
{
    path: 'some-path',
    component: SomeComponent,
    resolve: {
      defaultData: DefaultDataResolver
    }
}

Resolver

代码语言:javascript
复制
...
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Data> {
    return this.service.getData();
}

该组件是从不激活,因为它的依赖关系是从不解析的。

3. https://stackoverflow.com/a/36296015/1063354 --我发现了使用publishLast().refCount()的建议。

守则:

代码语言:javascript
复制
getCustomer() {
    return this.http.get('/someUrl')
        .map(res => res.json()).publishLast().refCount();
}

这满足了我对缓存和解析的要求,但是--我还没有找到一个干净整洁的解决方案--清除缓存。

我是不是遗漏了什么?有人能想出一种更好的方法来缓存反应性的可观测数据,以便能够在缓存的数据不再相关的情况下映射结果和刷新缓存的数据吗?

EN

回答 3

Stack Overflow用户

发布于 2017-11-14 10:57:13

这个简单的类缓存结果,以便您可以多次订阅.value,并且只发出一个请求。您还可以使用.reload()进行新的请求并发布数据。

你可以像这样使用它:

代码语言:javascript
复制
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);
});

来文提交人:

代码语言:javascript
复制
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);
  }

}
票数 2
EN

Stack Overflow用户

发布于 2017-04-14 02:10:23

有了选项3,为了允许缓存的清除,您可以将可观察到的内容分配给私有成员并返回它,例如。

代码语言:javascript
复制
getCustomer() {
    if (!this._customers) {
        this._customers = this.http.get('/someUrl')
        .map(res => res.json()).publishLast().refCount();
     }
     return this._customers
}

clearCustomerCache() {
    this._customers = null;
}
票数 0
EN

Stack Overflow用户

发布于 2017-04-14 14:49:08

我的缓存方法是将状态保持在还原器/扫描fn中:

编辑3:添加了一段代码,通过键盘事件使缓存失效。

编辑2windowWhen操作符也适用于任务,并允许以非常简洁的方式表示逻辑:

代码语言:javascript
复制
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只执行一次异步选项,并将结果缓存到可观察到的其他遗漏项上。

代码语言:javascript
复制
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)
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/43387655

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档