【问题标题】:Chaining HttpClient calls in Angular 6在 Angular 6 中链接 HttpClient 调用
【发布时间】:2018-11-30 09:20:44
【问题描述】:

我想我已经阅读了 100 多篇关于该主题的帖子,但我仍然无法弄清楚如何在 Angular 6 中使用 rxjs 链接两个 HttpClient 调用。

假设我有一个带有该签名的服务:

  GeoService {
      getState(): Observable<string> {
          return this.http.get<string>(stateURL);
      }
      getCities(state: string): Observable<string[]> {
          return this.http.get<string[]>(citiesURL + state);
      }
   }

我一辈子都不知道如何在我的组件中获取状态和相应的城市列表:

import { Observable } from 'rxjs';
import { map, flatMap, mergeMap, filter, switchMap } from 'rxjs/operators';

...

 ngOnInit() {

      this.svc.getState().
        pipe(map((state) => {
            this.state = state;
            return this.svc.getCities(state);
          }),
          mergeMap((cities) => this.cities = cities))
        ).
        subscribe(console.log('done'));

上面的代码是我 20 次以我能想到的各种方式组合 pipe/map/mergeMap/subscribe 的随机尝试之一......非常感谢一个工作示例:)

谢谢!

编辑:“可能重复”的帖子均不包含有效的实际示例

【问题讨论】:

  • 您可以通过将第二个(和第三个等)调用放入订阅前一个调用的 res 部分(如果不需要 res 值,则完成)来链接它们。例如:this.svc.getState().subscribe(res=&gt;{this.svc.getCities(res).subscribe(res=&gt;{},err=&gt;{},()=&gt;{})},err=&gt;{},()=&gt;{})
  • @ggradnig 我不会这么说,你可以从 http 调用的订阅部分做到这一点
  • this.svc.getState().pipe(mergeMap(state =&gt; this.svc. getCities(state))).subscribe(...)
  • @martin 您需要订阅 getCities() 调用,否则它实际上不会进行 http 调用

标签: rxjs angular6


【解决方案1】:

你可以这样做

this.svc.getState().pipe(
     tap(state=>this.state=state),
     switchMap(this.svc.getCities))
.subscribe(cities=>{
    //got the cities
})

map 操作符是用来转换发射值的,但是 tap 操作符是用来做一些事情而不修改 observable 的发射值。

注意 switchMap(this.svc.getCities) 等价于 switchMap(state=>this.svc.getCities(state)

【讨论】:

  • 您在 rxjs 运算符中分配了一个全局变量。这有点反模式......
  • 全局变量?在哪里?
  • this.state?理想情况下,您的 rxjs 运算符应该使用纯函数。
  • 好的,但是点击(新版本的“do”)操作符是为了副作用rxjs-dev.firebaseapp.com/api/operators/tap
  • 正确。但它仍然是一种反模式;)在这种情况下,使用标准地图运算符很容易解决。
【解决方案2】:

第 21 次尝试是正确的 ;-)

this.svc.getState().
    pipe(mergeMap((state) => {
        this.state = state;
        return this.svc.getCities(state);
    }),
    tap((cities) => this.cities = cities)))
.subscribe(() => console.log('done'));

链式 Observable 进入 mergeMap。你可以把它想象成:

首先,将传入的通知映射到 Observable,然后合并生成的“内部”Observable 到“外部”Observable

此外,如果您打算更改外部状态,请使用 tap 而不是 map

【讨论】:

  • 谢谢,我测试了我们的解决方案并且它有效。我接受了 Kevins 的回答,因为它更简单,并且不会从 mergeMap 内部更改状态。
【解决方案3】:

你快到了:

    this.svc.getState().
        pipe(
            mergeMap((state) => {
                return this.svc.getCities(state).pipe(map(cities => {
                    return { state: state, cities: cities }
                }));
            }),
        ).subscribe(stateAndCities => console.log(stateAndCities));

我建议你阅读这篇文章:

https://blog.strongbrew.io/rxjs-best-practices-in-angular/#using-pure-functions

它还解释了为什么您不应该在 rxjs 运算符中与全局变量进行交互。

【讨论】:

  • 谢谢,经过测试,它可以工作。我接受了凯文的回答,因为它不涉及中间对象。
  • 我确实理解你关于纯函数的观点,但我不喜欢为了将结果带到管道末端而不得不聚合成一个无意义的对象。对我来说很自然的模式是: var response = this.svc.getState(); response.subscribe(state => this.state = state); response.pipe(map(state => getProviders(state))).subscribe(cities => ...);伪代码,我厌倦了那个API,我没有勇气去测试它......
  • 可以,但是您的响应对象必须是 ReplaySubject(或应用了 publishReplay() 的 Observable)
猜你喜欢
  • 2019-04-24
  • 2018-11-22
  • 2019-08-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-04
  • 2018-12-29
  • 1970-01-01
相关资源
最近更新 更多