Angular 2 使用 observable 的力量缓存 http 请求

标签 angular rxjs angular2-services

我发现了很多缓存响应式可观察对象的方法,更具体地说,是缓存 http 请求的结果。但是,由于以下原因,我对提出的解决方案并不完全满意:

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

代码:

private data: Data;    
getData() {
    if(this.data) {
        return Observable.of(this.data);
    } else {
        ...
    }
}

可悲的是,可观察量的力量被完全忽略了——你手动完成所有的事情。事实上,如果我对将结果分配给局部变量/字段感到满意,我就不会寻找合适的解决方案。 另一个我认为不好的做法是服务不应该有状态——即不应该有包含数据的私有(private)字段,这些数据会随着调用而改变。而且 清除缓存 相当容易 - 只需将 this.data 设置为 null 即可重新执行请求。

2. 这个答案https://stackoverflow.com/a/36413003/1063354建议使用 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))

这是因为底层观察者没有调用它的complete 方法。我很确定很多响应式(Reactive)方法在这里也行不通。要实际获取数据,我必须订阅这个可观察对象,但我不想这样做:我想通过解析器 应该返回一个Observable(或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我找到了使用 publishLast().refCount() 的建议。

代码:

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

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

我错过了什么吗?谁能想出一种更好的方法来缓存响应式可观察对象,使其能够映射其结果并在不再相关时刷新缓存数据?

最佳答案

这个简单的类缓存结果,因此您可以多次订阅 .value 并且只发出 1 个请求。您还可以使用 .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);
  }

}

关于Angular 2 使用 observable 的力量缓存 http 请求,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43387655/

相关文章:

javascript - 在自定义服务中通过 Promise Angular 获取值(value)

html - 在 html 元素上只允许点击事件一次

javascript数组对象覆盖以前的对象,即使我正在创建新的引用

angularjs - ngbTypeahead 搜索结果丢失

javascript - Angular 绑定(bind)到 View 上的函数会导致对数据服务的无限调用

angular - 如何在 Angular2 中对成功的 HTTP 请求使用回调函数

html - 如何使大量文本适合 html 表格单元格

css - Bootstrap 3 - 如何将列表对齐到 iframe 的右侧

javascript - ionic : ERESOLVE could not resolve: Found: rxjs@7. x.x

javascript - rxjs - 具有下一个和错误回调的运算符?