Javascript Rxjs switchmap过早取消订阅

Javascript Rxjs switchmap过早取消订阅,javascript,reactjs,events,rxjs,Javascript,Reactjs,Events,Rxjs,我有这段代码跟踪位置变量,上游事件是键盘侦听器。我想发出“起始”位置,以便在任何真正的按键之前在屏幕上绘制播放器,因此我构建了这个可观察的,它以noop开始,以便在不移动的情况下发出起始位置: $keyboardEvent.pipe( startWith( { key: Key.Noop, keys: [Key.Noop], keyStroke: KeyStroke.Down, }, { key: Key

我有这段代码跟踪位置变量,上游事件是键盘侦听器。我想发出“起始”位置,以便在任何真正的按键之前在屏幕上绘制播放器,因此我构建了这个可观察的,它以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是在第一个事件通过管道传输后返回的


有人能帮我解释一下这种行为吗?如果有解决办法的话?我不希望使用一些缓冲的可观察对象,它的窗口刚好足够事件顺流,因为这似乎不是长期可维护的,而且我也不应该一开始就这么做。

继续评论中的讨论:我很确定发生了什么:

  • 第一个事件被传递到
    switchMap
    ,并订阅
    $animationEvent
  • $animationEvent
    发出任何值之前,第二个事件被传递到
    开关映射
    ,因此
    $animationEvent
    在有机会发出任何值之前被取消屏蔽
  • 第二个事件映射到
    EMPTY
    ,因此在
    switchMap
    之后不会发出任何消息

如果我做对了,那么您要做的就是添加起始事件,然后在管道的末尾添加
startWith
,它将只发送这两个起始事件作为前两个值。如果您看到它多次发出这两个值,这意味着
subscribe
被多次调用。一个容易导致这种情况的错误是,在
React.useffect
回调中订阅,并忘记添加
[]
作为第二个参数。

您好!如果它有助于直觉,那么我的想法是,当switchMap接收到一个新值时,它会将其视为一个信号,即基于以前的值所做的任何工作都应该废弃,并切换到处理新值。至于解决方法,这取决于您需要的行为-例如,如果用户的按键在动画进行过程中不应该做任何事情,只需将switchMap替换为exhaustMap。感谢您的快速回复!我要寻找的是,当订阅主可观察对象时,只发出一个起始值。在该起始值之后,键盘按下将启动一个动画循环,该循环将把玩家拉到与按键按下相关的位置,空闲后,可观察对象将完成(也称为切换为空,以便RAF停止处理)。如果我在开关映射后将startWith移动到,每次按键后它都会重置位置,这也是一种不好的行为。哦,我想我明白了!我会在回答中回答“有更多的空间”,稍后将发布。啊,看起来我对开关地图的工作原理做出了错误的假设,我假设开关地图之后的一切都是一个新的可观察的,在这种情况下,每下一个开关都会重新开始,而不是整个可观察的。这成功了,谢谢@贾贾巴尔(随时:)