IF/ELSE IF/ELSE 以 Observable 作为条件

IF/ELSE IF/ELSE with Observable as conditional

我正在尝试创建一种方法来 return 可以存储在三个位置之一的设置。我创建了三个可观察对象,每个都尝试从源中检索设置值。我希望能够 "join" 这些以创建 IF/ELSE IF/ELSE 构造的方式,以便最后只有一个 Observables 发出设置值。从一个 observable 转移到下一个 observable 的条件是前一个 "failed",即发出一个没有通过条件的值。尝试每个选项的顺序很重要。

到目前为止,我的方法看起来像这样:

getSetting(settingName: string): Observable<any> {

    const optionOne$ = this.getItemAtFirst(settingName).pipe(
        take(1),
        filter(setting => setting && this.isSettingValid(settingName, setting)));

    const optionTwo$ = this.getItemAtSecond().pipe(
        take(1),
        filter(setting => setting && this.isSettingValid(settingName, setting)));

    const optionThree$ = of(this.defaultSettingValue);

    return merge(optionOne$, optionTwo$, optionThree$);
}

这显然不行,因为merge没有提供我需要的选择效果

有什么建议吗?提前致谢!

它应该像链接一样实现。使用上面的代码,即使您从 IndexDB 中获取数据,您仍然会调用 MangoDB(等待数据)。我所做的不是让它变得复杂,而是将调用链接起来。但这完全取决于用例。在这个用例中,我想使用 flatMap 运算符链接调用会更好。

编辑:TL;DR

merge 替换为 concat 并将 first 添加到 concat 上的管道中:

concat(optionOne$, optionTwo$, optionThree$)
    .pipe(
        first()
    );

concat 按顺序订阅每个 observable,这意味着 optionOne$ 首先被订阅,它发出它的值,当它完成时它被取消订阅,然后 concat 订阅 optionTwo$,依此类推……直到所有可观察对象完成,然后最终将所有值作为数组发出。添加 first 只是缩短了这个过程,因为它在发出第一个值后完成了连接。

你确实需要检查 concat 中的所有可观察对象是否会完成,否则你可能会遇到其中一个永远不会发出或完成的情况,这意味着你永远不会得到concat 发出的值。

——

假设您的 isSettingValid() returns 如果验证失败则为 false,那么只需将 pipe()first() 添加到 concat()。请参阅下面稍作修改的版本,以及此处的 stackblitz

这个 article is also informative. And I suggest this article 有很酷的 gif 来解释运算符及其工作原理 - 当我试图弄清楚这样的事情时,我总是会回到它。

我认为这回答了您的问题,因为它发出第一个通过验证的值并忽略其余值。

import { of, Observable,  concat } from 'rxjs';
import { take, filter, first } from 'rxjs/operators';

function isSettingValid(setting): boolean {
  return setting !== 'error' ? true : false;
}

function getSetting(): Observable<any> {

  const optionOne$ = of('error')
    .pipe(
      take(1),
      filter(setting => isSettingValid(setting))
    );

  const optionTwo$ = of('second')
    .pipe(
      take(1),
      filter(setting => isSettingValid(setting))
    );

  const optionThree$ = of('default').pipe(
    take(1),
    filter(setting => isSettingValid(setting))
  );

  return concat(optionOne$, optionTwo$, optionThree$)
    .pipe(
      first() // emits only the first value which passes validation then completes
    );
}

getSetting()
  .subscribe((setting) => {
    console.log(setting);
  })

我还建议删除 setting &&,就像在我的代码片段中一样,并将 null 检查放在验证函数中 - 如果您这样做,它只会使过滤器更易于阅读 imo。为了使其更简洁,您可以考虑将 take(1)filter() 替换为 first 和一个具有相同结果的谓词函数:

const optionOne$ = of('error')
    .pipe(
      first(setting => isSettingValid(setting))
    );