【问题标题】:Angular2 observable share is not workingAngular2 可观察共享不起作用
【发布时间】:2025-12-31 06:35:16
【问题描述】:

Angular2 Observable 共享不起作用,重复的 http 调用正在进行

BuildingService.ts

@Injectable()
export class BuildingService {

constructor(private http: Http){       
  }

buildings$: Observable<Building[]>;
this.buildings: Building[];

getData() : Observable<Building[]>{
     this.buildings$ = this.http.get('http://localhost:8080/buildings').share().map(this.extractData);
     this.buildings$.subscribe(buildings => this.buildings = buildings);
     return this.buildings$;
  }

 private extractData(res: Response) {
    let body = res.json();
    return body;
} 

}

component1.ts

export class component1 {
constructor( private  buildingService: BuildingService) {}

this.subscription = this.buildingService.getData()
            .subscribe(buildings => console.log(buildings),
            error =>  this.errorMessage = <any>error);
}

component2.ts

export class component2 {
constructor( private  buildingService: BuildingService) {}

this.subscription = this.buildingService.getData()
            .subscribe(buildings => console.log(buildings),
            error =>  this.errorMessage = <any>error);
}

share 不工作,多个 http 调用正在进行。即使我尝试了来自this link的代码

但没用。

有人可以告诉我如何避免使用 Angular Observable 进行重复的 http 调用吗?

【问题讨论】:

标签: angular rxjs observable angular2-services rxjs5


【解决方案1】:

我认为这只是对 share() 所做的事情的误解。

当您调用this.buildings$.subscribe(...) 时,它会生成一个ConnectableObservable,这要归功于share() 运算符,紧随其后的是connect()

如果您在 HTTP 请求挂起时进行另一个订阅,它只会将另一个观察者添加到 ConnectableObservable,并且当响应准备好时,它将被发送到两个观察者。但是,如果您让 this.buildings$ 完成,然后您再次订阅,它将发出另一个 HTTP 请求,因为 ConnectableObservable 未连接到其源。

您想要的是 .publishReplay(1).refCount()(或自 RxJS 5.4.0 起为 shareReplay(1)),它重播从源发出的最后一个项目。您很可能还需要附加 take(1) 以正确完成链。

【讨论】:

  • 感谢您的回复。当我第二次执行 getData() 时,publishReplay(1).refCount() 甚至不允许 http 调用。由于我正在对返回的数据进行过滤,因此当我一直执行 getData() 时,我需要准确地进行一次后端调用。您能告诉我如何实现这一目标吗?
  • @PratapA.K 你能让 pnlkr 或 jsbin 重现你的问题吗?
  • 创建 pnlkr 对我来说非常困难,因为我有多个组件和单独的过滤器模块。我为我的另一个疑问创造了一个新问题。你能看一下吗?*.com/questions/41458117/…
  • @martin 现在你可以使用 .shareReplay(1) 了,它是 .publishReplay(1).refCount() 的别名
  • @JakubBarczyk True
【解决方案2】:

您在服务和组件中都订阅了 observable。试试这个作为你服务中的 getData 方法:

getData() : Observable<Building[]>{
 return this.http.get('http://localhost:8080/buildings')
    .map(this.extractData);
}

【讨论】:

  • 他正在使用 share()。这并不重要
  • 这将导致每个getData().subscribe()的新休息呼叫
【解决方案3】:

您正在通过每个.getData()-call 在这里创建一个新流:

this.buildings$ = this.http.get('http://localhost:8080/buildings').share().map(this.extractData);

如果你想在组件之间“共享”数据并防止多个 rest-calls,你很可能必须使用 rxjs 的 replay-feature,例如,你可以这样做:

@Injectable()
export class BuildingService {

constructor(private http: Http){}

buildings$: Observable<Building[]>;
this.buildings: Building[];

getData(fetchNew: boolean = false) : Observable<Building[]>{
     if (fetchNew || !this.buildings$) {
         this.buildings$ = this.http.get('http://localhost:8080/buildings')
             .map(this.extractData)
             .publishReplay(1)
             .refCount();
         this.buildings$.subscribe(buildings => this.buildings = buildings);
     }
     return this.buildings$;
  }

  private extractData(res: Response) {
    let body = res.json();
    return body;
  }
}

publishReplay(1) 将重新发送(“重播”)最后发送的数据给未来的订阅者,因此不会进行新的调用。

【讨论】:

  • 当我执行 getData() 时如何进行准确的后端调用。每次调用 getData() 方法时,我都需要进行后端调用。
  • 我理解您的问题,您确实希望对每个getData() 进行多次休息呼叫...您不能两次调用函数并说“但我只想做那部分一次”-您可以定义数据应保留多长时间(如此处:*.com/documentation/rxjs/8247/common-recipes/26490/…)-但我认为对于 proper 解决方案,您应该拆分逻辑数据获取和存储数据将为您提供更多灵活性 - 也许您想看看 ngrx:github.com/ngrx/store