【问题标题】:Angular 2 cache observable http result data [duplicate]Angular 2缓存可观察的http结果数据[重复]
【发布时间】:2017-05-24 01:50:03
【问题描述】:

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

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

有没有简单的解决方案?

【问题讨论】:

标签: angular observable angular2-services


【解决方案1】:

如果您倾向于将 observables 作为共享数据的一种方式,您可以采用以下方法:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

import { Observable, ReplaySubject } from 'rxjs';

import { SomeModel } from 'path/to/it';

@Injectable({
  providedIn: 'root'
})
export class CachedService {
 
  private dataSubject = new ReplaySubject<SomeModel>(1);
  
  data$: Observable<SomeModel> = this.dataSubject.asObservable();

  constructor(private http: HttpClient) { }

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

这将在调用fetch 方法时进行HTTP 调用,并且service.data$ 的任何订阅者都将从ReplaySubject 获得响应。由于它会重放较早的值,因此在 HTTP 调用解决后加入的任何订阅者仍将获得先前的响应。

如果您想触发更新,只需调用service.fetch() 启动新的 HTTP 调用,一旦新的响应到达,所有订阅者都会更新。

您的组件将如下所示:

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

  private subscription?: Subscription;

  constructor(private service: CachedService) { }

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

  ngOnDestroy() {
    if (this.subscription) {
      this.subscription.unsubscribe();
    }
  }
}

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

【讨论】:

  • 嗨,谢谢。我现在知道了。实际上我使用了 BehavoirSubjects 并将其初始化为 null。但是 onInite 事件在服务中不起作用,而是另一个问题(不是真的无能为力)。
  • ngOnInit 是一个生命周期钩子。如果我没记错的话,服务没有生命周期钩子。你应该在构造函数中调用 this.fetch() 而不是使用 ngOnInit
  • 嗨,如何使用调度程序实现一个不错的缓存驱逐过程?每小时调用一次 fetch() 服务?
  • @ClementMartino 你的意思是清除缓存?我不确定,我从来没有这样做过。
  • @Stefdelec 然后写另一个答案表明,我并不是说这是唯一的方法。
【解决方案2】:

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

相反,我们希望利用 rxjs 通过流无缝地向我们传递缓存的值 - 调用者无需了解有关缓存值和非缓存值的任何信息。

如果一个组件需要一个数据,它订阅它,不管它是否缓存。你为什么要 fetch() 一个你不确定它会被使用的数据?

缓存应该在更高的层次上实现。我认为这种实现是一个好的开始: 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;
}

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

【讨论】:

    【解决方案3】:

    您可以构建简单的类 Cacheable 来帮助管理从 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();
        }
    
    }
    

    用法

    声明可缓存对象(大概是服务的一部分):

    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

    【讨论】:

    • 什么时候真正使用缓存?
    猜你喜欢
    • 1970-01-01
    • 2019-05-21
    • 1970-01-01
    • 1970-01-01
    • 2018-03-30
    • 1970-01-01
    • 2020-06-26
    • 2017-12-04
    • 1970-01-01
    相关资源
    最近更新 更多