如何在多个组件中重用角度可观察性? (为了避免重复的请求两次?)

问题描述:

我有一个服务“MyHttpService”,其包括可观察到的是这样的:如何在多个组件中重用角度可观察性? (为了避免重复的请求两次?)

grabData() { 
return this.http.get('myaddress') 
         .map((res:Response) => {return res.json()}) 
         .catch((error:any) => Observable.throw(error.json().error || 'Server error')); 
} 

我有2个部件。 OneComponent和TwoComponent都注入了“MyHttpService”,这取决于MyHttpService返回的数据。

OneComponent首先加载。按钮单击后,TwoComponent加载。

内onNgInit()每个组件的我:

 this.myHttpService.grabData() 
     .subscribe(
       data => { 
// do something to the data 
}); 

它是正确的假设,即使我有这个认购的两个组成部分,有不会是所谓的多个HTTP请求和当TwoComponent加载时,对“grabData()”的调用与OneComponent已经提取的数据相同吗?或者它会发起一个新的呼叫?我想避免多个HTTP请求到同一个端点。如果每次调用具有此功能的组件时都进行多个调用,那么处理此问题的最佳方法是什么?以便每次初始化TwoComponent时都不会有多次调用该服务?

Http服务返回所谓的冷观察对象。这意味着每个新用户都将导致该观察对象的工作再次完成,在Http的情况下,将进行网络请求。

幸运的是,RxJS中有一些机制可以让您发布(也称为多播或多路复用)观察点,以便多个订阅者不会导致多个请求。

我通常对Http这样做的方法是使用.publishReplay(1).refCount()对运算符。 .publishReplay(1)意味着以后的用户可以马上获得最新的成功价值,而不会提出其他请求。 .refCount()表示第一个用户提出请求,最后一个用户取消订阅清理原始Http可观察。

版本5.4.0添加了这两个运算符的快捷方式,即.shareReplay(1)

只需在grabData()服务方法的末尾打上任一版本,它就应该像您的愿望那样工作。

grabData() { 
    return this.http.get('myaddress') 
        .map((res:Response) => {return res.json()}) 
        .catch((error:any) => 
         Observable.throw(error.json().error || 'Server error')) 
        .publishReplay(1).refCount(); // or .shareReplay(1) 
} 
+0

_和最后一个取消订阅清理原来的Http observable._ - 正确,所以如果一些其他订阅被做'HTTP'请求再次执行,正确?所以基本上http请求的结果将被共享,直到至少有用户,然后请求会被重复。 'AsyncSubject'似乎是更好的选择,你怎么看? –

+1

@Maximux,你能分享一下AsyncSubject方法的意思吗?基于我在使用上述方法时看到的情况,看起来像当我单击按钮时,客户端使用同一个服务“grabData()”发出第二个请求,而不是使用共享/第一个“缓存”值请求。 – Rolando

当您在TwoComponent中订阅时,它将导致再次发送http请求。阻止多个http调用的最好方法是将响应数据存储在您的服务中,并在http调用之前,检查您是否已经保存了数据,如果是,直接返回数据。

+0

我不希望downvote,但是这是要少得多习惯使用RxJS的,当网络请求返回之前由第二订阅不处理的情况。我试图坚持的建议是最大限度地减少或消除手动订阅服务中的观察值,最好是您的组件。相反,使用运算符组合observables并使用'async'管道在视图中使用它们。 – GregL

+1

@GregL我需要更多地深入RxJS,但我高举了你的答案。我理解你的担心是按照我所建议的方式进行的,并且如果有直接通过RxJS提供的方式,那肯定会是更好的选择。 – SimplyComplexable

您可以使用share运算符来创建可观察的多播。这在单个组件中也是相关的,因为对同一个osbervable的多个异步绑定会导致多个请求。有一个很好的ng-conf谈话here涵盖了这个问题。

import 'rxjs/add/operator/share'; 

grabData() { 
    return this.http.get('myaddress') 
        .map((res:Response) => {return res.json()}) 
        .catch((error:any) => Observable.throw(error.json().error || 'Server error')) 
        .share(); 
}