Angular 2缓存可观察的http结果数据

时间:2017-01-09 18:06:09

标签: angular observable angular2-services

我有一个通过HTTP服务获取数据并返回一个可观察对象的服务。

在第一次调用之后,我想在服务内部缓存结果,一旦新组件尝试获取数据,它将从缓存结果中获取数据。

这有一个简单的解决方案吗?

3 个答案:

答案 0 :(得分:45)

如果您将可观察量作为共享数据的手段,则可以采用以下方法:

import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';

import { Observable, ReplaySubject } from 'rxjs';

@Injectable()
export class CachedService {
  data$: Observable<Response> = this.dataSubject.asObservable();

  private dataSubject = new ReplaySubject<Response>(1);

  constructor(private http: Http) { }

  fetch() {
    this.http.get(...).subscribe(res => this.dataSubject.next(res));
  }
}

这将在调用fetch方法时进行HTTP调用,service.data$的所有订阅者都将从ReplaySubject获得响应。当它重放之前的值时,在 HTTP调用解析后加入的任何订阅者仍将获得先前的响应。

如果您想触发更新,只需拨打service.fetch()即可启动新的HTTP呼叫,并在新响应到达后更新所有订阅者。

您的组件看起来像是:

@Component({ ... })
export class SomeComponent implements OnInit {

  constructor(private service: CachedService) { }

  ngOnInit() {
    this.service.fetch();
    this.service.data$.subscribe(...);
  }
}

我最近为同事写了一篇关于这种方法的博客文章:http://blog.jonrshar.pe/2017/Apr/09/async-angular-data.html

答案 1 :(得分:9)

我认为你不应该在构造函数中或在angular的生命周期中的任何时间执行fetch()。正如你所说,ngOnInit在角度服务中不起作用。

  

相反,我们希望利用rxjs通过流无缝传递缓存值 - 调用者不必知道有关缓存与非缓存值的任何信息。

如果组件需要数据,则无论是否为高速缓存,它都会对其进行订阅。你为什么要获取()你不确定它将被使用的数据?

缓存应该在更高级别实现。我认为这种实现是一个良好的开端: http://www.syntaxsuccess.com/viewarticle/caching-with-rxjs-observables-in-angular-2.0

getFriends(){
    if(!this._friends){
      this._friends = this._http.get('./components/rxjs-caching/friends.json')
                                   .map((res:Response) => res.json().friends)
                                   .publishReplay(1)
                                   .refCount();
    }
    return this._friends;
}

我不确定这是最好的方法,但它更容易维护,因为它只有一个责任。只有当一个组件订阅它时,数据才会被缓存,无论什么/谁/哪个组件需要数据,并且是第一个需要它的人。

答案 2 :(得分:2)

您可以构建简单的类Cacheable&lt;&gt;这有助于管理从http服务器或其他任何其他来源检索的数据的缓存:

declare type GetDataHandler<T> = () => Observable<T>;

export class Cacheable<T> {

    protected data: T;
    protected subjectData: Subject<T>;
    protected observableData: Observable<T>;
    public getHandler: GetDataHandler<T>;

    constructor() {
      this.subjectData = new ReplaySubject(1);
      this.observableData = this.subjectData.asObservable();
    }

    public getData(): Observable<T> {
      if (!this.getHandler) {
        throw new Error("getHandler is not defined");
      }
      if (!this.data) {
        this.getHandler().map((r: T) => {
          this.data = r;
          return r;
        }).subscribe(
          result => this.subjectData.next(result),
          err => this.subjectData.error(err)
        );
      }
      return this.observableData;
    }

    public resetCache(): void {
      this.data = null;
    }

    public refresh(): void {
      this.resetCache();
      this.getData();
    }

}

<强>用法

声明可缓存&lt;&gt;对象(可能是服务的一部分):

list: Cacheable<string> = new Cacheable<string>();

和处理程序:

this.list.getHandler = () => {
// get data from server
return this.http.get(url)
.map((r: Response) => r.json() as string[]);
}

从组件调用:

//gets data from server
List.getData().subscribe(…)

更多详细信息和代码示例如下:http://devinstance.net/articles/20171021/rxjs-cacheable