在RxJs 5中,分享Angular Http网络调用的结果的正确方法是什么?

351

通过使用 Http,我们调用执行网络调用并返回 http observable 的方法:

getCustomer() {
    return this.http.get('/someUrl').map(res => res.json());
}

如果我们将此可观察对象添加多个订阅者:

let network$ = getCustomer();

let subscriber1 = network$.subscribe(...);
let subscriber2 = network$.subscribe(...);
我们想要做的是确保这不会导致多个网络请求。
这可能看起来像一个不寻常的场景,但实际上很常见:例如,如果调用者订阅可观察对象以显示错误消息,并使用异步管道将其传递到模板中,则我们已经有了两个订阅者。
在 RxJs 5 中正确的做法是什么?
也就是说,这似乎还不错:
getCustomer() {
    return this.http.get('/someUrl').map(res => res.json()).share();
}

但是在RxJs 5中,这是惯用方法吗?还是我们应该选择其他方法?

注意:根据Angular 5的新HttpClient,所有示例中的.map(res => res.json())部分现在已经无用了,因为默认情况下假定为JSON结果。


1
share()与publish().refCount()并不完全相同。请参考以下讨论:https://github.com/ReactiveX/rxjs/issues/1363 - Christian
1
修改问题,根据问题看起来代码文档需要更新 -> https://github.com/ReactiveX/rxjs/blob/master/src/operator/share.ts - Angular University
我认为“这取决于情况”。但是对于那些无法在本地缓存数据的调用,因为由于参数的更改/组合可能没有意义,使用.share()似乎绝对是正确的选择。但是,如果您可以在本地缓存某些内容,则关于ReplaySubject / BehaviorSubject的其他答案也是不错的解决方案。 - JimB
我认为我们不仅需要缓存数据,还需要更新/修改缓存的数据。这是一个常见的情况。例如,如果我想要向缓存的模型中添加一个新字段或更新字段的值。也许创建一个带有CRUD方法的单例__DataCacheService__是更好的方式?就像Redux的__store__一样。你觉得呢? - Lin Du
你可以简单地使用 ngx-cacheable!它更适合你的场景。请参考我下面的回答。 - Tushar Walzade
请考虑给@Arlo的答案点赞。当我使用多个订阅和combineLatest()时,我只需要在我的管道中添加shareReplay(1)作为最后一个操作符,请求就会在单个“调用堆栈”内共享。 - al-bex
22个回答

0
只需在map之后和任何subscribe之前调用share()
在我的情况下,我有一个通用的服务(RestClientService.ts),它进行REST调用,提取数据,检查错误并将可观察对象返回给具体的实现服务(例如:ContractClientService.ts),最后这个具体的实现服务将可观察对象返回给ContractComponent.ts,然后该组件订阅以更新视图。

RestClientService.ts:

export abstract class RestClientService<T extends BaseModel> {

      public GetAll = (path: string, property: string): Observable<T[]> => {
        let fullPath = this.actionUrl + path;
        let observable = this._http.get(fullPath).map(res => this.extractData(res, property));
        observable = observable.share();  //allows multiple subscribers without making again the http request
        observable.subscribe(
          (res) => {},
          error => this.handleError2(error, "GetAll", fullPath),
          () => {}
        );
        return observable;
      }

  private extractData(res: Response, property: string) {
    ...
  }
  private handleError2(error: any, method: string, path: string) {
    ...
  }

}

ContractService.ts:

export class ContractService extends RestClientService<Contract> {
  private GET_ALL_ITEMS_REST_URI_PATH = "search";
  private GET_ALL_ITEMS_PROPERTY_PATH = "contract";
  public getAllItems(): Observable<Contract[]> {
    return this.GetAll(this.GET_ALL_ITEMS_REST_URI_PATH, this.GET_ALL_ITEMS_PROPERTY_PATH);
  }

}

ContractComponent.ts:

export class ContractComponent implements OnInit {

  getAllItems() {
    this.rcService.getAllItems().subscribe((data) => {
      this.items = data;
   });
  }

}

-5
你尝试过运行你已经有的代码吗?
因为你正在从getJSON()返回的promise构造Observable,所以在任何人订阅之前就会发出网络请求。而且所有订阅者都共享这个结果的promise。
var promise = jQuery.getJSON(requestUrl); // network call is executed now
var o = Rx.Observable.fromPromise(promise); // just wraps it in an observable
o.subscribe(...); // does not trigger network call
o.subscribe(...); // does not trigger network call
// ...

我已经编辑了问题,使其更具体地针对Angular 2。 - Angular University

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接