【问题标题】:Rxjs switchmap cancels subscription too earlyRxjs switchmap过早取消订阅
【发布时间】:2020-09-26 21:18:14
【问题描述】:

我有这段代码来跟踪位置变量,上游事件是键盘侦听器。我想发出“开始”位置,以便在进行任何真正的按键操作之前将播放器绘制在屏幕上,所以我构建了这个 observable,它以 noop 开始,以便在没有任何移动的情况下发出开始位置:

$keyboardEvent.pipe(
    startWith(
      {
        key: Key.Noop,
        keys: [Key.Noop],
        keyStroke: KeyStroke.Down,
      },
      { key: Key.Noop, keys: [], keyStroke: KeyStroke.Up }
    ),
    tap(({ keys }) => (keysRef.current = keys)),
    switchMap(() => (keysRef.current.length > 0 ? $animationEvent : EMPTY)),
    map(() => {
      if (keysRef.current.length > 0) {
        playerPosition.current = updatePlayerPosition(keysRef.current);
        playerRotation.current = updatePlayerRotation(keysRef.current);
      }

      playerMovementState.current =
        keysRef.current.length > 0 ? "active" : "idle";

      return {
        position: playerPosition.current,
        rotation: playerRotation.current,
        state: playerMovementState.current,
      };
    })
  )

问题似乎是 switchMap 操作符在第二个启动事件通过管道后立即取消 $animationEvent,这似乎发生在第一个事件可以发送给订阅者之前。当我在 switchMap 之前点击事件时,这是输出:

{key: "", keys: Array(1), keyStroke: "Down"}
{key: "", keys: Array(0), keyStroke: "Up"}

当我在 switchMap 之后点击时,我没有得到任何结果,即使我知道 $animationEvent 在第一个事件通过管道后返回。

有人可以帮忙解释一下这种行为吗?是否有任何解决方法?我宁愿不必使用一些缓冲的 observable,它的窗口足够大,足以让事件发生在下游,因为这似乎无法长期维护,而且我不应该一开始就这样做。

【问题讨论】:

  • 您好!如果它有助于直觉,我的想法是,当 switchMap 接收到一个新值时,它会将其作为一个信号,即基于先前值所做的任何工作都应该被废弃,并切换到处理新值。至于解决方法,它只取决于您需要什么行为 - 例如,如果用户的按键在动画进行时不应该做任何事情,只需将 switchMap 替换为排气映射。
  • 感谢您的快速回复!我正在寻找的是在订阅主 observable 时发出一个起始值。在该起始值之后,键盘按下将启动一个动画循环,该循环将把玩家拉到与按键相关的位置,并且在空闲之后,observable 将完成(也就是切换到 EMPTY 以便 RAF 停止处理)。如果我将 startWith 移到 switch map 之后,它只会在每次按键后重置位置,这也是不好的行为。
  • 哦,我想我明白了!我会回复一个答案以获得更多空间,稍后会发布。

标签: javascript reactjs events rxjs


【解决方案1】:

继续在 cmets 中进行讨论:我很确定正在发生的事情是这样的:

  • 第一个事件被传递给switchMap$animationEvent 被订阅。
  • $animationEvent 发出任何值之前,第二个事件被传递给switchMap,因此$animationEvent 在它有机会触发任何值之前被取消订阅。
  • 第二个事件映射到EMPTY,所以最后在switchMap之后什么都没有发出。

如果我做对了,并且您要做的只是添加起始事件,那么在管道末尾添加 startWith 是可行的方法 - 它只会发出这两个起始事件作为第一个两个值。如果您看到它不止一次发出这两个值,这意味着 subscribe 被多次调用。一个很容易犯的错误就是订阅React.useEffect 回调并忘记添加[] 作为第二个参数。

【讨论】:

  • 啊,看来我对 switchMap 的工作方式做出了错误的假设,我假设 switchMap 之后的所有内容都是一个新的 observable,在这种情况下 startWith 会在每个下一个 switchTo observable 再次发生,并且不是作为一个整体的可观察的。成功了,谢谢!
  • @jajabarr 随时:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-03-26
  • 2020-10-28
  • 1970-01-01
  • 2021-11-18
  • 1970-01-01
  • 2016-07-07
  • 2018-07-29
相关资源
最近更新 更多