RxJS iif 参数在不应调用时被调用

23
我想要使用 RxJS 中的 iif 工具来有条件地触发一些操作。问题在于,即使测试函数返回 false,iif 的第二个参数也会被调用。这将导致错误并立即使应用程序崩溃。我对 RxJS 的能力还比较陌生,所以可能不知道某些事情。另外,如果有影响的话,我正在使用 connected-react-router 包。

iif 的第二个参数应该在测试函数返回 false 时被调用。如果测试函数返回 true,则将调用第一个参数。请参阅此处的文档 - dmcgrandle
按照第一个参数来计算测试函数。第二个和第三个参数在测试后被调用。 - E1-XP
1
好的,谢谢你澄清。接下来,请仔细检查测试函数是否确实返回“true”。如果它返回除此以外的任何值,那么“iif”将将其评估为“false”。我建议你尝试用() => true替换当前的内容,看看是“iif”引起了问题还是你的测试逻辑有误。 - dmcgrandle
4个回答

76

我来晚了,但我发现 iif 的作用并不是执行其中一条路径,而是订阅其中一个 Observable 或另一个。也就是说,它会执行所有必要的代码路径以获取每个 Observable。

从这个例子来看...

import { iif, of, pipe } from 'rxjs';
import { mergeMap } from 'rxjs/operators';

const source$ = of('Hello');
const obsOne$ = (x) => {console.log(`${x} World`); return of('One')};
const obsTwo$ = (x) => {console.log(`${x}, Goodbye`); return of('Two')};

source$.pipe(
  mergeMap(v =>
    iif(
      () => v === 'Hello',
      obsOne$(v),
      obsTwo$(v)
    ))
).subscribe(console.log);

你将会得到以下输出

Hello World
Hello, Goodbye
One

这是因为,在获取 obsOne$ 时需要打印 "Hello World"。对于 obsTwo$ 也是如此(不同的是路径会打印 "Hello, Goodbye")。

然而,您会注意到它只打印了 One 而不是 Two。这是因为 iif 的值为 true,从而订阅了 obsOne$

虽然你的三目运算符可以工作,但我发现这篇文章很好地解释了一种使用 RxJS 实现所需结果的方法:https://rangle.io/blog/rxjs-where-is-the-if-else-operator/


10
iif()执行方式的解释很棒。 - Homer
9
非常好的解释! 有一种方法可以解决这个问题,就是使用defer。因此,如果您执行iif(条件, defer(() => obsOne$), defer(() => obsTwo$)),则只会执行与该条件对应的路径。 - Samuel Bushi
@SamuelBushi 谢谢!但实际上应该是 iif(() => condition, defer(() => obsOne$), defer(() => obsTwo$) - Leonardo Rick
晚来的派对。但这并不是关于Observables和iif的问题,而更多地是关于iif到底是什么。它只是一个函数。当调用一个函数时,它需要所有的参数。当其中一个参数本身是一个函数调用时,那么这个函数必须被调用。这就是为什么obsOne$()obsTwo$()总是被调用的原因。 - El Ronnoco

7

好的,我自己找到了答案。我的解决方案是完全删除iif并仅依靠mergeMap内部的三元运算符。这样它不会在每个“LOCATION_CHANGE”之后被评估,只有当regExp返回true时才会评估。感谢您的关注。

export const roomRouteEpic: Epic = (action$, state$) =>
  action$.ofType(LOCATION_CHANGE).pipe(
    pluck<any, any>('payload'),
    mergeMap(payload =>
      /^\/room\/\d+$/.test(payload.location.pathname)
        ? of(
            state$.value.rooms.list[payload.location.pathname.split('/')[2]]
              ? actions.rooms.initRoomEnter()
              : actions.rooms.initRoomCreate(),
            actions.global.setIsLoading(true),
          )
        : EMPTY,
    ),
  );

1
如果你在可观察对象创建中使用tap操作符(因为它返回void),会导致以下错误。
Error: You provided 'function tapOperatorFunction(source) {
return source.lift(new DoOperator(nextOrObserver, error, complete));
}' where a stream was expected. You can provide an Observable, Promise, Array, or Iterable.

移除水龙头并将控制台放在subscribe()中。

我已经创建了一个stackblitz演示


0
另一个需要考虑的因素是,即使在使用 RxJS 时 Observables 和 Promises 可以在相同的上下文中使用多次,但它们在处理 iif 时的行为将会有所不同。如上所述,iif 有条件地进行 订阅,而不是有条件地执行。我有这样一段代码:

.pipe(
    mergeMap((input) =>
        iif(() => condition,
            functionReturningAPromise(input),  // A Promise!
            of(null)
        )
    )
)

这是评估Promise返回函数的结果,而不管条件如何,因为Promises在运行时不需要订阅。我通过切换到一个if语句(三元运算符也可以)来修复它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接