【问题标题】:Multiple subscriptions nested into one subscription多个订阅嵌套在一个订阅中
【发布时间】:2020-02-13 07:32:59
【问题描述】:

我对设置一个非常简单的 rxjs 订阅流感到困惑。将多个不相关的订阅嵌套到另一个中。

我在一个 Angular 应用程序中,在进行其他订阅之前,我需要先填写一个主题。

这将是我想要实现的嵌套版本。

subject0.subscribe(a => {

    this.a = a;

    subject1.subscribe(x => {
        // Do some stuff that require this.a to exists
    });

    subject2.subscribe(y => {
        // Do some stuff that require this.a to exists
    });

});

我知道嵌套订阅不是好习惯,我尝试使用 flatMapconcatMap 但并没有真正了解如何实现这一点。

【问题讨论】:

  • 我认为投票否决这个问题的人至少需要解释他为什么这样做。这是您在使用订阅和可观察对象时可能会提出的问题。

标签: rxjs observable rxjs6 subject angular-observable


【解决方案1】:

您可以使用concat 运算符来做到这一点。

const first = of('first').pipe(tap((value) => { /* doSomething */ }));
const second = of('second').pipe(tap((value) => { /* doSomething */ }));
const third = of('third').pipe(tap((value) => { /* doSomething */ }));

concat(first, second, third).subscribe();

这样,所有内容都按照定义的顺序链接和执行。

编辑

const first = of('first').pipe(tap(value => {
  // doSomething
  combineLatest(second, third).subscribe();
}));
const second = of('second').pipe(tap(value => { /* doSomething */ }));
const third = of('third').pipe(tap(value => { /* doSomething */ }));
first.subscribe();

这样,secondthirdfirst 发出后立即异步运行。

【讨论】:

  • 感谢您的回复!我认为这会奏效,但我不希望一切都被束缚。第二个和第三个订阅应该只等待第一个,第三个不应该等待第二个。
【解决方案2】:

将每个 Observable 的数据流分开总是一个好主意,这样您以后就可以轻松地将它们组合起来。

const first$ = this.http.get('one').pipe(
  shareReplay(1)
)

shareReplay 用于使 Observable ,因此它不会在每次订阅时调用 http.get('one')

const second$ = this.first$.pipe(
  flatMap(firstCallResult => this.http.post('second', firstCallResult))
);

const third$ = this.first$.pipe(
  flatMap(firstCallResult => this.http.post('third', firstCallResult))
);

之后,您可以订阅所需的 Observable:

second$.subscribe(()=>{}) // in this case two requests will be sent - the first one (if there were no subscribes before) and the second one

third$.subscribe(() => {}) // only one request is sent - the first$ already has the response cached

如果您不想将first$ 的值存储在任何地方,只需将其转换为:

this.http.get('one').pipe(
  flatMap(firstCallResult => combineLatest([
    this.http.post('two', firstCallResult),
    this.http.post('three', firstCallResult)
  ])
).subscribe(([secondCallResult, thirdCallResult]) => {})

您也可以使用BehaviorSubject 将值存储在其中:

const behaviorSubject = new BehaviorSubject<string>(null); // using BehaviorSubject does not require you to subscribe to it (because it's a hot Observable)
const first$ = behaviorSubject.pipe(
  filter(Boolean), // to avoid emitting null at the beginning
  flatMap(subjectValue => this.http.get('one?' + subjectValue))
)

const second$ = first$.pipe(
  flatMap(firstRes => this.http.post('two', firstRes))
)

const third$ = first$.pipe(
  flatMap(()=>{...})
)

behaviorSubject.next('1') // second$ and third$ will emit new values
behaviorSubject.next('2') // second$ and third$ will emit the updated values again

【讨论】:

  • 谢谢,我认为第一个选项就是我要找的。在您的第二个选项中使用combineLatest 似乎不符合我的需要,因为两个和三个不应该互相等待。您的第三个选项非常有趣,即使我会选择 ReplaySubject(1) 以避免过滤初始空值。
【解决方案3】:

你可以这样做:

subject$: Subject<any> = new Subject();
this.subject$.pipe(
        switchMap(() => subject0),
        tap(a => {
            this.a = a;
        }),
        switchMap(() => subject1),
        tap(x => {
            // Do some stuff that require this.a to exists
        }),
        switchMap(() => subject2),
        tap(y => {
            // Do some stuff that require this.a to exists
        })
    );

如果你想触发这个,只需调用 this.subject$.next();

编辑: 这是使用 forkJoin 的一种可能方法,即喊叫主题并行。

subject$: Subject<any> = new Subject();
    this.subject$.pipe(
        switchMap(() => subject0),
        tap(a => {
            this.a = a;
        }),
        switchMap(
            () => forkJoin(
                subject1,
                subject2
        )),
        tap([x,y] => {
          // Do some stuff that require this.a to exists
        })
    );

【讨论】:

  • 我觉得在这个例子中,subject2会在订阅之前等待subject1。在我的情况下,我只需要 subject1 和 subject2 来等待 subject0,但不应该链接 subject1 和 subject2。是这样吗?
  • 是的,你可能是对的。我认为您可以将 subject1 和 subject2 放入 forkJoin。我更新我的答案。但我还没有测试这个呢
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-10-17
  • 2020-08-18
  • 1970-01-01
  • 1970-01-01
  • 2019-08-09
  • 1970-01-01
  • 2021-07-14
相关资源
最近更新 更多