繁体   English   中英

Rxjs - 链 http 调用与 Observables

[英]Rxjs - Chain http calls with Observables

我想做 3 个 http 调用,它返回一个 boolean 值。 我想要的是仅在上一次调用的结果为true时才进行下一次调用

IE:
call1() -> 如果为false ,则停止,如果为true
call2() -> 如果为false ,则停止,如果为true
call3()

现在以指示哪个调用返回false的方式读取结果。

我第一次尝试使用 switchMap:

    this.call1()
      .pipe(
        switchMap((r1) => (r1 ? this.call2() : of(false))),
        switchMap((r2) => (r2 ? this.call3() : of(false)))
      )
      .subscribe((r) => {
        console.log('inside subscribe:', r);
      });

现在在订阅中我得到的问题是:
'inside subscribe:' false
而且我不知道错误结果是来自 call1,2 还是 3。

所以我的解决方案是将结果保存在外部数组中,如下所示:

    // seriel execution with switchMap, we can use each result to the next call
    // + save all results
    const results = [false, false, false];
    this.call1()
      .pipe(
        // save stage1 result
        tap(r1 => results[0] = r1),
        // if true, make call2, else return false
        switchMap((r1) => (r1 ? this.call2() : of(false))),
        // save stage2 result
        tap(r2 => results[1] = r2),
        // if true, make call3, else return false
        switchMap((r2) => (r2 ? this.call3() : of(false))),
        // save stage3 result
        tap(r3 => results[2] = r3),
      )
      .subscribe((r) => {
        console.log('inside subscribe:', r); // r is the latest value results[2]
        console.log('all results:', results) // array of all stages [true,false,false]
      });

  }

它似乎有效,但也许还有另一种无需外部工作的方法? 仅与 rxjs 运营商合作?

StackBlitz 链接:
https://stackblitz.com/edit/angular-rxjs-playground-zjnh7u?devtoolsheight=33&file=app/app.component.ts

也许这样的事情可以工作

this.call1()
  .pipe(
    switchMap((r1) => (r1 ? this.call2() : 
             of(false).pipe(tap(() => console.log('r1 returns false')),
    switchMap((r2) => (r2 ? this.call3() : 
             of(false).pipe(tap(() => console.log('r2 returns false'))),
    tap(respOfR3 => if(!respOfR3) {console.log('r3 returns false')})
  )
  .subscribe((r) => {
    console.log('inside subscribe:', r);
  });

同时考虑你的第一个 Observable 通知的布尔序列隐含地包含你正在寻找的信息。 实际上,您遇到的第一个 false 的 position 反映了第一个返回false的调用。

我会将源附加到结果 - 最简单的是元组[number, boolean] ,其中元组/数组中的第一个元素是源。 switchMap解决方案的一个变体是:

    this.call1()
      .pipe(
        map((result) => [1, result]),
        switchMap((r1) => (r1[1] ? this.call2().pipe(map((result) => [2, result])) : of(r1))),
        switchMap((r2) => (r2[1] ? this.call3().pipe(map((result) => [3, result])) : of(r2)))
      )
      .subscribe((r) => {
        console.log('inside subscribe:', r);
      });

这有一个模式并且有点冗长,我们可以做得更好:

    function attachSource(source: number, target: Observable<boolean>): Observable<[number, boolean]> {
      return target.pipe(map((result) => [source, result]));
    }

    attachSource(1, this.call1())
      .pipe(
        switchMap((r1) => (r1[1] ? attachSource(2, this.call2()) : of(r1))),
        switchMap((r2) => (r2[1] ? attachSource(3, this.call3()) : of(r2)))
      )
      .subscribe((r) => {
        console.log('inside subscribe (try2):', r);
      });

如果我们再概括一点,给定以下内容:

    function attachSource<T>(source: number, target: () => Observable<T>): Observable<[number, T]> {
      return target().pipe(map((result) => [source, result]));
    }

    function series<T>(failed: (arg: T) => boolean, ...factories: (() => Observable<T>)[]): Observable<[number, T]> {
      return factories.reduce((agr, cur, index) => {
        if (index === 0) {
          return attachSource(index+1, cur);
        } else {
          return agr.pipe(switchMap((prevResult) => failed(prevResult[1]) ? of(prevResult) : attachSource(index+1, cur)));
        }
      }, empty<[number, T]>());
    }

它可以很简单:

    series((x: boolean) => x, this.call1.bind(this), this.call2.bind(this), this.call3.bind(this))
      .subscribe((r) => {
        console.log('inside subscribe (try3):', r);
      });

这是一个非常干净的解决方案,使用扩展运算符进行递归调用

const results = [false, false, false];
let count = 0;
this.call().pipe(
    tap(response => {
      results[count] = response
      count++
      }
    ),
    expand((response) => response && count < 3
               ? this.call()
               : EMPTY
    )
).subscribe((r) => {
    console.log('inside subscribe:', r); // r is the latest value results[2]
    console.log('all results:', results) // array of all stages [true,false,false]
});

而且你真的不需要多次调用 function (call1, call2, call3),一个就够了

call(): Observable<boolean> {
 let bool: boolean = this.randBool();
 return of(bool);
}

private randBool(): boolean {
  return Math.random() < 0.5
}

https://stackblitz.com/edit/angular-rxjs-playground-zvfpay?file=app/app.component.ts

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM