如何避免嵌套rxJS订阅

时间:2019-05-16 15:59:37

标签: angular typescript rxjs

我开始了一个新的Angular 7项目,我需要在其中调用一个API以获取结果列表。然后,如果列表中有特定结果,则选择它,否则,我将调用另一个API端点来创建它。所有这些内容都应放入ngOnInit()方法中。

为简化问题,我有以下代码:

const subject = new BehaviorSubject(null);
subject.next(6);

const res$ = subject.pipe(
  mergeMap(() => getValues()),
  tap((res) => {
    const exists = res.find(x => subject.value === x) || null;
    if (exists === null) {
      // TODO: call the getNew() function, but I don't want nested call
    } else {
      // Do nothing
    }
  })
);

// res$ should contains [1, 2, 3, 4, 5, 6]
res$.subscribe(x => console.log(x));

function getValues() {
  // Mocking the API call
  return of([1, 2, 3, 4, 5]);
}

function getNew() {
  // Mocking the API call
  return of(6);
}

最后,可观察的res$应该包含所有数组(具有增加的值),并且我想避免嵌套订阅。

非常感谢!

3 个答案:

答案 0 :(得分:2)

两件事:

  1. 不要指望可以访问主题的值。相反,您可以使用mergeMap的替代形式,该形式允许您从源流和新流中创建选择值。在这种情况下,我使用它从内部可观察对象的源和值中返回一个新项目。

  2. 通过将原始结果包装在mergeMap中以创建新的内部可观察对象,您可以仅使用另一个of来返回原始结果或新api调用的结果。

const res$ = subject.pipe(
  mergeMap(x => getValues(), (orig, res) => ({ orig, res })),
  mergeMap(({ orig, res }) => {
    const exists = res.find(x => x === orig) || null;
    return (exists) 
      ? of(res)
      : getNew().pipe(map(x => [... res, x]))
  })
);

Stackblitz

答案 1 :(得分:0)

解决此问题的一种方法是将其分为两个可观察对象,然后再将它们与CombineLatest合并。我们将使用可观察到的resFirst $两次,并根据是否为null对其进行过滤。

import { combineLatest } from 'rxjs';

---

const subject = new BehaviorSubject(null);
subject.next(6);

const resFirst$ = subject.pipe(
    mergeMap(() => getValues()),
    map((res) => res.find(x => subject.value === x) || null)
);

const resSecond$ = resFirst$.pipe(
    filter(exists => exists === null), <--- FILTERING FOR IS NULL
    mergeMap(() => getNew()) <--- SWITCHMAP COULD ALSO BE USED HERE DEPENDING ON WHAT YOU WANT
);

const final$ = combineLatest(resFirst$, resSecond$)
    .pipe(filter(exists => exists !== null)) <--- FILTERING FOR NOT NULL
    .subscribe(x => console.log(x));

function getValues() {
    // Mocking the API call
    return of([1, 2, 3, 4, 5]);
}

function getNew() {
    // Mocking the API call
    return of(6);
}

答案 2 :(得分:0)

const res$ = subject.pipe(
  mergeMap(x => getValues().pipe(
    mergeMap(values => values.find(v => v === x) 
      ? of(values)
      : getNew().pipe(map(n => [...values, n]))
    )
  ))
);

https://stackblitz.com/edit/rxjs-ch5iao