这个问题在这里已有答案:
我有一个服务,通过HTTP服务获取数据并返回一个可观察对象 .
在第一次调用之后,我想在服务内部缓存结果,一旦新组件尝试获取数据,它将从缓存结果中获取它 .
有一个简单的解决方案吗?
如果您将可观察对象作为共享数据的手段,则可以采用以下方法:
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调用解析后加入的任何订阅者仍将获得先前的响应 .
fetch
service.data$
ReplaySubject
如果要触发更新,只需调用 service.fetch() 即可启动新的HTTP呼叫,一旦新响应到达,所有订阅者都将更新 .
service.fetch()
您的组件将看起来像:
@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
我认为你不应该在构造函数中或在angular的生命周期中的任何时间执行fetch() . 正如你所说, ngOnInit 在角度服务中不起作用 .
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; }
我不确定这是最好的方法,但它更容易维护,因为它有一个单一的责任 . 只有当一个组件订阅它时,数据才会被缓存,无论什么/谁/哪个组件需要数据并且是第一个需要它的人 .
您可以构建简单的类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(); } }
Usage
声明Cacheable <>对象(可能是服务的一部分):
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
3 回答
如果您将可观察对象作为共享数据的手段,则可以采用以下方法:
这将在调用
fetch
方法时进行HTTP调用,service.data$
的任何订阅者都将从ReplaySubject
获得响应 . 当它重放之前的值时,在HTTP调用解析后加入的任何订阅者仍将获得先前的响应 .如果要触发更新,只需调用
service.fetch()
即可启动新的HTTP呼叫,一旦新响应到达,所有订阅者都将更新 .您的组件将看起来像:
我最近为同事写了一篇关于这种方法的博客文章:http://blog.jonrshar.pe/2017/Apr/09/async-angular-data.html
我认为你不应该在构造函数中或在angular的生命周期中的任何时间执行fetch() . 正如你所说,
ngOnInit
在角度服务中不起作用 .如果组件需要数据,则无论是否为高速缓存,它都会对其进行预订 . 为什么要获取()您不确定它将被使用的数据?
缓存应该在更高级别实现 . 我认为这种实施是一个良好的开端:http://www.syntaxsuccess.com/viewarticle/caching-with-rxjs-observables-in-angular-2.0
我不确定这是最好的方法,但它更容易维护,因为它有一个单一的责任 . 只有当一个组件订阅它时,数据才会被缓存,无论什么/谁/哪个组件需要数据并且是第一个需要它的人 .
您可以构建简单的类Cacheable <>,它有助于管理从http服务器或其他任何其他来源检索的数据的缓存:
Usage
声明Cacheable <>对象(可能是服务的一部分):
和处理程序:
从组件调用:
更多细节和代码示例如下:http://devinstance.net/articles/20171021/rxjs-cacheable