【问题标题】:IF/ELSE IF/ELSE with Observable as conditionalIF/ELSE IF/ELSE 以 Observable 为条件
【发布时间】:2019-07-01 11:08:02
【问题描述】:

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

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

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 没有提供我需要的选择效果。

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

【问题讨论】:

  • 为什么将相同的东西存放在三个不同的地方?对于任何想要改变这一点的人来说,这肯定会非常混乱
  • @Liam 我没有详细说明,但这些地方是本地存储或 MongoDB。我需要先在本地检查(IndexedDB),然后如果设置不存在,我尝试 MongoDB,如果不存在,我使用默认值。
  • 似乎forkJoin 是要走的路
  • 但是我只想在第一个不发射的情况下才去寻找第二个或第三个可观察对象......看起来 forkJoin 不像那样工作 - 看起来它执行了所有涉及的可观察对象。
  • 已经提供了答案,但我认为@Liam 的意思是您将删除过滤器,并使用 forkJoin 获取所有值,然后整理它们是否有效它们来自哪个来源?我的回答假设您从中获取值的可观察对象将完成-即使单个未完成,合并也不会触发……这是要考虑的相关内容,但超出了您的要求这里

标签: angular typescript rxjs observable rxjs6


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    编辑:TL;DR

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

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

    concat 按顺序订阅每个 observable,这意味着 optionOne$ 首先被订阅,它发出它的值,当它完成时,它被取消订阅,然后 concat 订阅 optionTwo$,等等......直到所有 observables 完成,然后最终将所有值作为数组发出。添加first 只会缩短这个过程,因为它会在第一个值发出后完成连接。

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

    ——

    假设您的isSettingValid() 在验证失败时返回 false,那么只需将带有first()pipe() 添加到concat()。请参阅下面稍作修改的版本,并在此处stackblitz

    这个article 也很丰富。我推荐这个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 &amp;&amp;,就像在我的 sn-p 中一样,并将空检查放在您的验证函数中 - 如果您这样做,只会使过滤器更容易阅读 imo。为了更简洁,您可以考虑将take(1)filter() 替换为first 和一个具有相同结果的谓词函数:

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

    【讨论】:

    • 感谢您的回答。我已经实现了与此类似的东西,但我看到对于 optionOne 和 optionThree 都通过 isSettingValid 检查的情况,返回的是 optionThree。这不是我需要的结果,我需要返回optionOne的方法。
    • 然后尝试 concat 而不是 merge。
    • 对不起,我没有发现您需要 optionOne,只是您想要第一个通过的选项。如果通过验证,Concat 应确保您首先获得 optionOne。
    • 请务必查看我链接到的第二篇文章并阅读有关 concat 的内容。当你看到 gif 时就会明白。
    • concat工作就像一个魅力:-) 谢谢!我读了这篇文章,用图片理解这一点真的很有帮助:-)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-06
    相关资源
    最近更新 更多