首页 > 解决方案 > IF/ELSE IF/ELSE 以 Observable 作为条件

问题描述

我正在尝试创建一种方法来返回可以存储在三个位置之一的设置。我创建了三个可观察对象,每个对象都尝试从源中检索设置值。我希望能够以这样的方式“加入”这些以创建一个 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没有提供我需要的选择效果。

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

标签: angulartypescriptrxjsobservablerxjs6

解决方案


编辑:TL;DR

替换为mergeconcat添加first到您的管道concat

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

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

您确实需要检查concatwill 中的所有 observables 是否完成,否则您可能会遇到其中一个永远不会发出或完成的条件,这意味着您永远不会获得从concat.

——

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

这篇文章也很翔实。我推荐这篇文章,它有很酷的 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 &&, 就像在我的代码段中一样,并将空检查放在您的验证函数中 - 如果您这样做,它只会使过滤器更容易阅读 imo。为了使其更简洁,您可以考虑将take(1)andfilter()替换为firstand 一个具有相同结果的谓词函数:

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

推荐阅读