Angular7 + RxJ的多个订户可观察相同

时间:2019-04-25 22:34:59

标签: angular rxjs angular7 rxjs6

这可能是一个菜鸟问题,但我可以在上面找到参考。

我有两个放置在不同位置的组件,每个组件都使用Service查询一个端点并获取一些数据(例如,用户个人资料)。

该服务返回一个可观察对象,我需要该可观察对象是唯一的,或者至少要发出一个唯一的请求。

// Service, shared across multiple components
@Injectable()
export class SomeService {
    getData(): Observable<DataModel> {
        return this._http.get<DataResponse>('/some-route').pipe(
          map((response: DataResponse) => this.handleResponse(response)),
          share() // is this right ?
        );
    }

}

// Component requesting data
@Component({ selector: '...', ... })
export class FirstComponent implements OnInit {
  constructor(private _service: SomeService) { }

    ngOnInit() {
        this._service.getData().subscribe(
          data => {
            console.log(data);
          }
        );
      }
}

// Another component requesting the same data
@Component({ selector: '...', ... })
export class SecondComponent implements OnInit {
  constructor(private _service: SomeService) { }

    ngOnInit() {
        this._service.getData().subscribe(
          data => {
            console.log(data);
          }
        );
      }
}

该服务正常工作并获取数据,但是请求发送了两次,我只希望发送一个请求。组件同时处于活动状态(假设一个在屏幕的顶部,第二个在屏幕的底部)。因此,他们会同时发出请求。

服务是否只能发送一个请求?

顺便说一句,第一个请求的状态为200,第二个状态为304。

谢谢。

更新

可能的解决方案

到目前为止,我通过添加Service变量进行管理

private _observable: Observable<DataModel>;

然后在获取数据时

getData(): Observable<DataModel> {
    if (!this._observable) {
        this._observable = this._http.get<DataResponse>('/some-route').pipe(
          map((response: DataResponse) => this.handleResponse(response)),
          // HERE IS THE TRICK
          publishLast(),
          refCount()
        )
    }

    return this_observable;
}

诀窍是使用publishLast和refCount

有什么更好的方法/想法吗?

4 个答案:

答案 0 :(得分:1)

看看我的库ngx-rxcache。简化了状态管理。

https://github.com/adriandavidbrand/ngx-rxcache

@Injectable()
export class SomeService {
 private dataCache = this.cache.get<DataModel>({
    id: 'some uniquie id',
    load: true,
    construct: () => this.http.get<DataResponse>('/some-route').pipe(
      map((response: DataResponse) => this.handleResponse(response))
    )
 });

 data$ = this.dataCache.value$;

 constructor(cache: RxCacheService, http: HttpClient) {}
}

然后您可以使用来访问服务上的数据

this.service.data$;

https://medium.com/@adrianbrand/angular-state-management-with-rxcache-468a865fc3fb

答案 1 :(得分:0)

您可以使用forkJoin运算符轻松地将您的请求分组为一个Observable,然后在完成所有请求后就将其解析

这是一个例子

  public requestDataFromMultipleSources(): Observable<any[]> {
    let response1 = this.http.get(requestUrl1);
    let response2 = this.http.get(requestUrl2);
    let response3 = this.http.get(requestUrl3);
    // Observable.forkJoin (RxJS 5) changes to just forkJoin() in RxJS 6
    return forkJoin([response1, response2, response3]);
  }

在您的示例中,您应该在父组件中发出请求。然后将结果注入子组件

答案 2 :(得分:0)

您可以改用 shareReplay ,这将从所有订阅者观察源重播事件,并确保仅共享最后一个结果

 shareReplay(1)

因此您的代码将如下所示

getData(): Observable<DataModel> {
    return this._http.get<DataResponse>('/some-route').pipe(
      map((response: DataResponse) => this.handleResponse(response)),
      shareReplay(1) 
    );
}

答案 3 :(得分:0)

我发现以下模式可以有效地做到这一点:

@Injectable()
export class SomeService {
  myDataObservable$: Observable<DataModel>;

  constructor( ) {
    this.myDataObservable$ = this.getData().pipe(shareReplay());
  }

  getData(): Observable<DataModel> {
    return this._http.get<DataResponse>('/some-route').pipe(
      map((response: DataResponse) => this.handleResponse(response))
    );
  }
}
  1. 在服务上做一个属性(myDataObservable $),该属性包含将由多个组件使用的可观察对象。
  2. 具有执行http请求的方法(getData())。
  3. 在构造函数中,调用方法并添加shareReplay()。
  4. 在组件中,将其与“ this.someService.myDataObservable $ .subscribe(...)”一起使用

在这种模式下,在至少一个组件中的myDataObservable $上调用subscription之前,不会发送任何HTTP请求。在第一个组件调用了subscribe之后,所有后续的subscription将使用已经存在的值,因此您不会有多个http请求。