Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/angular/30.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Angular 2缓存http请求,使用可观察的能力_Angular_Rxjs_Angular2 Services - Fatal编程技术网

Angular 2缓存http请求,使用可观察的能力

Angular 2缓存http请求,使用可观察的能力,angular,rxjs,angular2-services,Angular,Rxjs,Angular2 Services,我已经找到了许多方法来缓存反应性观察,更具体地说,缓存http请求的结果。然而,我对提议的解决方案并不完全满意,原因如下: 1。此答案使用专用字段存储第一个请求的结果,并在所有后续调用中重复使用 守则: private data: Data; getData() { if(this.data) { return Observable.of(this.data); } else { ... } } getCustomer() {

我已经找到了许多方法来缓存反应性观察,更具体地说,缓存http请求的结果。然而,我对提议的解决方案并不完全满意,原因如下:

1。此答案使用专用字段存储第一个请求的结果,并在所有后续调用中重复使用

守则:

private data: Data;    
getData() {
    if(this.data) {
        return Observable.of(this.data);
    } else {
        ...
    }
}
getCustomer() {
    return this.http.get('/someUrl')
        .map(res => res.json()).publishLast().refCount();
}
可悲的是,可观测的力量被完全忽略了——所有的事情都是手工完成的。事实上,如果我对将结果赋给局部变量/字段感到满意,我就不会寻找合适的解决方案。 另一个重要的事情,我认为一个坏的做法是,服务不应该有一个国家-即不应该有私人字段包含的数据,改变了从呼叫到呼叫。清除缓存非常容易,只需将this.data设置为null,请求就会重新执行

2.此答案建议使用ReplaySubject:

    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))
这是因为基础观察者没有调用其完整方法。我很确定,很多被动的方法在这里也不起作用。要实际获取数据,我必须订阅此可观测数据,但我不想这样做:我想通过解析程序获取某个组件的缓存数据,该解析程序应返回可观测数据,而不是订阅:

路线

{
    path: 'some-path',
    component: SomeComponent,
    resolve: {
      defaultData: DefaultDataResolver
    }
}
解析程序

...
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Data> {
    return this.service.getData();
}
这满足了我对缓存和解析的要求,但是我还没有找到一个干净整洁的解决方案来清除缓存


我错过什么了吗?有没有人能想出一种更好的方法来缓存被动观测值,以便能够映射其结果,并在不再相关时刷新缓存的数据?

使用选项3。为了允许清除缓存,您可以将可观察对象分配给私有成员并返回,例如

getCustomer() {
    if (!this._customers) {
        this._customers = this.http.get('/someUrl')
        .map(res => res.json()).publishLast().refCount();
     }
     return this._customers
}

clearCustomerCache() {
    this._customers = null;
}

我的缓存方法是在reducer/scan中保持状态fn:

编辑3:添加一段代码以通过键盘事件使缓存无效

编辑2
窗口when
运算符也适用于该任务,并允许以非常简洁的方式表达逻辑:

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);
它将仅每隔5秒执行一次异步选项,并将结果缓存在可观察对象上,以防其他遗漏

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)

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

您可以像这样使用它:

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);
  }

}
导出类RestResource{
静态只读加载:字符串='RestResource_LOADING';
静态只读错误:字符串='RestResource_ERROR';
静态只读空闲:字符串='RestResource_IDLE';
公共价值:可见;
公众地位:可见;
私人地位:观察员;
私人装载机:可观察;
私有重载触发器:观察者;
构造函数(requestObservableFn:()=>Observable){
this.status=Observable.create((o)=>{
这是loadStatus=o;
});
this.reloader=Observable.create((o:observator)=>{
this.reloadTrigger=o;
});
this.value=this.reloader.startWith(null).switchMap(()=>{
if(this.loadStatus){
this.loadStatus.next(RestResource.LOADING);
}
returnrequestobservefn()
.map((res)=>{
if(this.loadStatus){
this.loadStatus.next(RestResource.IDLE);
}
返回res;
}).catch((错误)=>{
if(this.loadStatus){
this.loadStatus.next(RestResource.ERROR);
}
可观测的返回值(空);
});
}).publishReplay(1).refCount();
}
重新加载(){
this.reloadTrigger.next(null);
}
}

您的回答非常详尽,谢谢!但是有一件事:如果我要根据超时清除缓存(据我所知),我宁愿使用
publishReplay(11000)
而不是
publishLast()
,这会使缓存每1秒失效一次。这就是你想要展示的吗?如果是,那么这并不是我真正想要的——应该通过调用一个方法显式地清除缓存。你能重新编写你的例子,这样缓存就可以按需清除(没有超时逻辑)?@Ultimaho啊,我明白了。
clearCacheInterval$
中的间隔就是一个例子,它可以合并/替换为任何其他可见的间隔,例如鼠标点击。我改变了示例,当用户按下“c”键时,我也清除了缓存。我找到了整个方法,并尝试将observable设置为null。不幸的是,这对我不起作用-服务继续返回以前的值,而没有进行任何真正的http调用。你能解释一下这些操作是如何解决问题的吗(想法是什么)?“Observable”类型上不存在“Property”publishLast。“我如何解决此错误?”@Vivek try-import“rxjs/add/operator/publishLast”;可能重复的