编辑2:更新@karser's answer中所述的shareReplay(1)
效果更好。
编辑1:最终效果最好的是:
@Injectable()
export class MyGlobalService {
private resource$;
private resource$Connected;
constructor(private http: Http) {
this.resource$Connected = false;
this.resource$ = this.http
.get('/api/resource')
.map((res: Response) => res.json())
.publishReplay(1);
}
getResource(): Observable<any> {
if (!this.resource$Connected) {
this.resource$.connect();
this.resource$Connected = true;
}
return this.resource$;
}
}
它只会使AJAX调用一次,并且在某些消费者需要资源之前它不会调用。
原始问题:
我尝试缓存角度HTTP呼叫,并将最新结果多播到所有当前和未来的订阅者。 ajax结果不会在应用程序生命周期内发生变化,因此我不想对已有的资源进行任何额外调用。因此,我希望它继续保持联系&#34;即使所有订阅者都取消订阅。这可能吗?
我最初的尝试是:
// in a global service
getResource(): Observable<any> {
return this.http
.get('/api/resource')
.map((res: Response) => res.json())
.publishLast()
.refCount();
}
这适用于同一组件中的多个async
管道,但如果该组件被销毁(因此refCount
变为0),则在稍后的组件实例化时将重复HTTP请求
为了解决这个问题,我开始手动缓存结果:
resourceResults: any;
getResource(): Observable<any> {
if (resourceResults) {
return Observable.of(this.resourceResults);
}
return this.http
.get('/api/resource')
.map((res: Response) => res.json())
.do(x => this.resourceResults = x)
.publishLast()
.refCount();
}
这很好用,但我觉得有更多的方法可以做到。
我尝试过使用connect()
,但这似乎与我的第一个例子有同样的问题。一旦所有订阅者都取消订阅,使用connect()
会导致HTTP请求再次发生
resource$ = this.http
.get('/api/resource')
.map((res: Response) => res.json())
.publishLast()
.refCount();
getResource(): Observable<any> {
this.resource$.connect();
return this.resource$;
}
有什么想法吗?
答案 0 :(得分:1)
publishReplay / connect有效。这是the working plunker:
if ($databaseOject->num_rows > 0) {
echo "<table><tr><th>id</th><th>apartament</th><th>nume</th><th>apartament</th><th>persoane</th><th>mp</th><th>retim</th><th>incalzire</th><th>apacaldamc</th><th>apacaldalei</th><th>apacaldadif</th><th>aparecemc</th><th>aparecelei</th><th>aparecedif</th><th>curent</th><th>gaz</th><th>administrator</th><th>cheltuieliadministrare</th><th>acoperis</th><th>timp</th></tr>";
// output data of each row
while($row = $databaseOject->fetch_assoc()) {
echo "<tr><td>".$row["id"]."</td><td>".$row["apartament"]."</td><td>".$row["nume"]."</td><td>".$row["persoane"]."</td><td>".$row["mp"]."</td><td>".$row["retim"]."</td><td>".$row["incalzire"]."</td><td>".$row["apacaldamc"]."</td><td>".$row["apacaldalei"]."</td><td>".$row["apacaldadif"]."</td><td>".$row["aparecemc"]."</td><td>".$row["aparecelei"]."</td><td>".$row["aparecedif"]."</td><td>".$row["curent"]."</td><td>".$row["gaz"]."</td><td>".$row["administrator"]."</td><td>".$row["cheltuieliadministrare"]."</td><td>".$row["acoperis"]."</td><td>".$row["timp"]."</td></tr>";
}
echo "</table>";
} else {
echo "0 results";
}
输出:
import {Injectable} from '@angular/core';
import {Http, Response} from '@angular/http';
import {Observable} from "rxjs/Observable";
@Injectable()
export class YourService {
resource:Observable<any>;
constructor(private http: Http) {
this.resource = this.http.get('https://api.github.com/users/karser')
.map((res: Response) => res.json())
.do(res => console.log('response', res))
.publishReplay(1);
this.resource.connect();
}
}
更新:RxJS 5.4有shareReplay运算符,显然做同样的事情。见the updated plunkr
Subscribing
response Object {login: "karser", id: 1675033…}
Unscibscribed
Subscribing once again
来自pull request:
this.http.get('https://api.github.com/users/karser') .map((res: Response) => res.json()) .shareReplay(1);
返回一个可被多播的源的观察者 超过shareReplay
。该重播主题在出错时被回收 来源,但不是在完成源。这使得ReplaySubject
非常适合处理缓存AJAX结果之类的事情 它是可以重试的。然而,它的重复行为与分享不同 它不会重复源可观察,而是会重复 来源可观察的价值观。
答案 1 :(得分:0)
首先,如果您想在整个应用程序生命周期内共享此响应,则应将其放入服务中,并确保所有组件共享同一个实例。
理论上你可以使源Observable永远不会完成。更准确地说,您可以使链条永远不会传播完整的信号,但是如果您尝试将此Observable与forkJoin()
或toArray()
等运算符一起使用,则不建议这样做并可能导致意外行为类似的,要求源Observable正确完成。
相反,您可以使用publishReplay(1)
来保持源Observable发出的最后一个值refCount()
在查询缓存时保持对源Observable的单个订阅,然后take(1)
只接受一个值并完成。
const cache = this.http.get('/api/resource')
.map((res: Response) => res.json())
.publishReplay(1)
.refCount()
.take(1);
因此,您可以一次订阅多个观察者,并且只会执行一个HTTP请求。然后,任何后续订阅都将点击已经具有缓存值的.publishReplay(1)
,该值立即传播,take(1)
立即完成链。因此,不会订阅this.http.get('/api/resource')
,因此不会提出任何请求。
答案 2 :(得分:0)
不要使用refCount。手动创建可连接的可观察对象,对其进行连接,订阅以及可连接的ReplaySubject将保留最后发布的值。这样的事情:
let obs$ = Rx.Observable.interval(5000)
.multicast(new Rx.ReplaySubject(1));
obs$.connect();
// unsuscribe when service is destroyed???
let mainSubecription = obs$.subscribe(x=>console.log(x));
// create subscription (like async pipe)
let subscription;
setTimeout(()=> {
subscription = obs$.subscribe(x=>console.log('second', x));
}, 15000);
// remove subscription (like async pipe)
setTimeout(()=> {
subscription.unsubscribe();
}, 40000);
// the main subscription still gets data