简体   繁体   English

链接两个以上的Angular Observable调用

[英]Chaining more than two Angular Observable calls

I'm currently working on an Angular 5 service, and I need to chain four separate Observable calls, with each subsequent call using the data from some or all previous calls . 我目前正在使用Angular 5服务,并且需要链接四个单独的Observable调用,每个后续调用都使用部分或全部先前调用中的数据。 I've seen some examples of combining two calls, but I don't see how to use more than two (and trying to extend them, via flatMap , has resulted in the data from previous calls not being available in subsequent calls). 我已经看到了合并两个调用的一些示例,但是我看不到如何使用两个以上的示例(尝试通过flatMap扩展它们导致先前调用中的数据在后续调用中不可用)。

  • CallOne returns Observable<Foo[]> CallOne返回Observable<Foo[]>
    • I then need to do some filtering to pick a specific Foo 然后,我需要进行一些过滤以选择特定的Foo
  • CallTwo requires a Foo and returns Observable<FooTwo[]> CallTwo需要Foo并返回Observable<FooTwo[]>
    • I then need to do some filtering to pick a specific FooTwo 然后,我需要进行一些筛选以选择特定的FooTwo
  • CallThree requires a FooTwo and returns Observable<FooThree[]> CallThree需要一个FooTwo并返回Observable<FooThree[]>
    • I then need to do some filtering to pick a specific FooThree 然后,我需要进行一些筛选以选择特定的FooThree
  • CallFour requires a Foo , a FooTwo , and a FooThree and returns Observable<Bar[]> CallFour需要FooFooTwoFooThree并返回Observable<Bar[]>

Afterwards, I need access to the chosen Foo , FooTwo , FooThree and a specific Bar . 之后,我需要访问选择的FooFooTwoFooThree和特定的Bar

You can use mergeMap and forkJoin , with forkJoin you can fire parallel request at time and it will till all request completed. 您可以使用mergeMapforkJoin ,使用forkJoin可以同时触发并行请求,直到所有请求完成为止。

Observable.forkJoin(
    call1(params),
    call2(params),
    call3(params)
).subscribe((responses) => {
    // responses[0] -> response of call1
    // responses[1] -> response of call2
    // responses[2] -> response of call3
})

However if you want to make it synchronous and make the request dependent on the previous call, you can do like this, 但是,如果您想使其同步并使其请求依赖于上一个调用,则可以这样做,

const request1$ = Rx.Observable.of('response1').delay(2000);
const request2$ = Rx.Observable.of('response2').delay(100);

Rx.Observable.forkJoin(request1$, request2$)
  .subscribe(res => console.log(`forkJoin: ${res}`));

Handling Observables that depend on each other 处理彼此依赖的可观察对象

  • CallOne returns Observable<Foo[]> CallOne返回Observable<Foo[]>
    • I then need to do some filtering to pick a specific Foo 然后,我需要进行一些过滤以选择特定的Foo
  • CallTwo requires a Foo and returns Observable<FooTwo[]> CallTwo需要Foo并返回Observable<FooTwo[]>
    • I then need to do some filtering to pick a specific FooTwo 然后,我需要进行一些筛选以选择特定的FooTwo
  • CallThree requires a FooTwo and returns Observable<FooThree[]> CallThree需要一个FooTwo并返回Observable<FooThree[]>
    • I then need to do some filtering to pick a specific FooThree 然后,我需要进行一些筛选以选择特定的FooThree
  • CallFour requires a Foo , a FooTwo , and a FooThree and returns Observable<Bar[]> CallFour需要FooFooTwoFooThree并返回Observable<Bar[]>

Based on the above spec: 根据以上规格:

callOne().pipe(
    switchMap((foos: Foo[]) => {
         // filtering foos to get Foo
         this.Foo = <filtered_foo>;
         return callTwo(<filtered_foo>);
    }),
    switchMap((foos2: FooTwo[]) => {
         // filtering foos2 to get FooTwo
         this.FooTwo = <filtered_foo_two>;
         return callThree(<filtered_foo_two>);
    }),
    switchMap((foos3: FooThree[]) => {
         // filtering foos3 to get FooThree
         this.FooThree= <filtered_foo_three>;
         return callFour(this.Foo, this.FooTwo, <filtered_foo_three>);
    })
).subscribe((bars: Bar[]) => {
    this.bars = bars;
    /**
     * this.Foo, this.FooTwo, this.FooThree and this.bars will all be available here
     */
})

I'm not sure if this is the most efficient way to solve this, but this is what worked for me: 我不确定这是否是解决此问题的最有效方法,但这对我有用:

 return this.callOne()
  .flatMap(foo => {
    // foo filter logic here
    return this.callTwo(foo[0].id).map(fooTwo => ({ foo, fooTwo }));
  })
  .flatMap(({ foo, fooTwo }) => {
    // fooTwo filter logic here
    return this.callThree(fooTwo[0].id).map(fooThree => ({ foo, fooTwo, fooThree }));
  })
  .flatMap(({ foo, fooTwo, fooThree }) => {
    return this.callFour(fooTwo[0].id, fooThree[0]).map(bar => ({ foo, fooTwo, fooThree, bar }));
  })
  .do(({ foo, fooTwo, fooThree, bar }) => {
    // additional processing here
    return ({ foo, fooTwo, fooThree, bar });
  });

Since you have so many values you need to keep track of, I would really suggest you use Subject . 由于您需要跟踪许多值,因此我建议您使用Subject That is the exact purpose of Subject - to retain the values. 那是Subject的确切目的-保留值。 In your case for example you can use BehaviourSubject . 例如,您可以使用BehaviourSubject

First, declare the subject needed: 首先,声明所需的主题:

let call1Subject = new BehaviourSubject<Foo[]>(null);
let call2Subject = new BehaviourSubject<FooTwo[]>(null);
let call3Subject = new BehaviourSubject<FooThree[]>(null);

Now, you can update the values of the BehaviourSubject using .next , and you can get its most current value via .value() : 现在,您可以使用.next更新BehaviourSubject的值,并可以通过.value()获得其最新值:

return this.callOne()
    .flatMap(foo => {
        // foo filter logic here
        call1Subject.next(foo);
        return this.callTwo(foo[0].id);
    })
    .flatMap(fooTwo => {
        // fooTwo filter logic here
        call2Subject.next(fooTwo);
        return this.callThree(fooTwo[0].id);
    })
    .flatMap(fooThree => {
        call3Subject.next(fooThree);
        let fooTwo = call2Subject.value();
        return this.callFour(fooTwo[0].id, fooThree[0]);
    })
    .map(bar => {
        // additional processing here

        let foo = call1Subject.value();
        let fooTwo = call2Subject.value();
        let fooThree = call3Subject.value();

        return ({foo, fooTwo, fooThree, bar});
    });

The code looks much much neater, and you have a very clear context. 该代码看起来更加整洁,并且您具有非常清晰的上下文。 The .map() way works too; .map()方法也可以工作; but your code can easily get bloated and its harder to manage. 但是您的代码很容易变得肿并且难以管理。

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

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