【问题标题】:Angular - Observables in servicesAngular - 服务中的 Observables
【发布时间】:2017-09-24 19:48:09
【问题描述】:

我对 Angular 场景还很陌生,我开始因一些意想不到的行为而烦恼。

我的集成服务中有以下代码

queryList(): Observable<ApiList[]> {
    if(this.loadingApiList == true) {
      return Observable.of(this.queryListResult);
    }
    this.loadingApiList = true;
    return this._http
      .get('samples/apiList.json').map( (response: Response) => <ApiList[]> response.json())
      .do(data => {
        this.queryListResult = data;
      });
}

如何阻止在从路由加载页面时执行两次此调用?

一个组件调用此方法并在导航中构建一个列表,另一个组件也为以下方法调用相同的服务:

getApiName(IT_ID:number) {
    console.log(this.queryListResult);
    let obj = this.queryListResult.find(o => o.IT_ID == IT_ID);
    if(obj) {
      return obj.IT_Name;
    }
    return false;
}

这在我不使用路由时非常有效,但是当我使用路由时,getApiName 返回一个未定义的结果。

我也使用了 resolve 对象,这确实解决了问题,但随后对 json 文件进行了两次调用,这就是我使用的。

  resolve(route: ActivatedRouteSnapshot, state:RouterStateSnapshot):Observable<any>{
    return this._service.queryList() // We need this to finish first.
  }

我现在真的被困住了——我可以使用 resolve 让它工作,但是对 .json 进行两次调用很浪费。

这样做的正确方法是什么?

【问题讨论】:

  • 设置了flag并不一定意味着操作完成,查询列表结果已经赋值。实现此目的的一种方法是在基于主题的可观察对象上公开数据并触发一次提取,例如参见blog.jonrshar.pe/2017/Apr/09/async-angular-data.html 我展示了我们是如何使用这种模式的。

标签: angular rxjs


【解决方案1】:

如何阻止此调用在从路由加载页面时执行两次?

这是因为您订阅了两次。 Observables 是惰性的,每次你用.subscribe() 调用一个,它就会开始执行:在你的情况下,它会触发一个网络请求。将 observables 中的 .subscribe() 想象为 .call() 或简单地将 () 用于函数:每次调用函数时,它都会执行。

考虑到这一点,您遇到的行为是预期的。有不同的方法可以解决这个问题,它们相似但根据您的用例略有不同; IE。订阅和取消订阅的确切时间。通常,您要查找的内容称为multicasting

最简单的多播形式是使用the .shared() operator。来自official docs;但请注意重点(我的):

返回一个多播(共享)原始 Observable 的新 Observable。 只要有至少一个订阅者,这个 Observable 就会被订阅并发出数据。当所有订阅者都取消订阅后,它将取消订阅源 Observable。因为 Observable 正在多播,所以它使流变热。这是.publish().refCount() 的别名。

您可能感兴趣的是创建一个BehvaiorSubject,因为它具有“当前值”的概念。那么您的 API 调用将不会返回任何内容,它只会触发请求。

这是一个概念验证:

export class Service {

  public data$ = new BehaviorSubject(null);

  public get() {
    this.http.get(`some/data`).subscribe(data => this.data$.next(data))
  }

}

null 是每个订阅者在订阅data$ 时将同步获得的值。你可以这样使用它。

this.get()
this.data$.subscribe(data => /* ... */)

当然,你可以把它包装成更好的东西,甚至开始实现缓存之类的东西。


一些有用的链接:

【讨论】:

  • 感谢您的详细解答。由于每个组件调用 get() ,使用 BehaviorSubject 会不会导致两个请求相同的问题?
  • 没错,这就是我说的原因,我在这里给出的设置只是理解BehaviorSubject 的起点。这个想法是您应该处理使用自定义逻辑调用该端点的频率。您不希望 never 再次调用它,因为数据可能会过时。从 RxJS 的角度来看,就它所知,你所做的这两个调用可能是不同的结果。您可以以某种方式缓存响应并确定响应是否有效。
  • 为了清楚起见,如果我在服务的 ngOnInit 上调用 get() 以确保数据存在。在此 get() 之前和之后订阅的所有组件都会在使用订阅时收到更新的数据副本?
  • 刚刚测试过——您先生是个天才。感谢您的帮助。
猜你喜欢
  • 2018-10-06
  • 2017-02-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-24
  • 1970-01-01
  • 1970-01-01
  • 2018-09-14
相关资源
最近更新 更多