Redux 满足条件时暂停epic,然后在条件完成时发出缓冲操作

Redux 满足条件时暂停epic,然后在条件完成时发出缓冲操作,redux,rxjs,redux-observable,Redux,Rxjs,Redux Observable,我正在开发一个应用程序,当用户在页面之间导航时,我会定期将信息保存到服务器 目前,我们通过安排一个“persist”操作来实现这一点,该操作在完成一个“persist_end”操作之前传播一系列事件。目前,如果用户快速导航,这些分组操作可能会相互拦截,从而导致各种问题。我想我可以缓冲开始的动作,然后等到结束的动作被执行 我使用Redux Observables站点的乒乓球示例创建了一个类似的示例: 前提是类似的,我允许用户随时按“开始PING”按钮,收听此消息的epic应检查当前是否有PING操

我正在开发一个应用程序,当用户在页面之间导航时,我会定期将信息保存到服务器

目前,我们通过安排一个“persist”操作来实现这一点,该操作在完成一个“persist_end”操作之前传播一系列事件。目前,如果用户快速导航,这些分组操作可能会相互拦截,从而导致各种问题。我想我可以缓冲开始的动作,然后等到结束的动作被执行

我使用Redux Observables站点的乒乓球示例创建了一个类似的示例:

前提是类似的,我允许用户随时按“开始PING”按钮,收听此消息的epic应检查当前是否有PING操作(由“actionPauser”BehaviorSubject执行),并将任何操作排队,直到前一个PING操作完成

epic应该发出最新的缓冲动作,因此它过滤缓冲列表,然后通过最新的动作

我似乎无法理解的是——控制台日志指示页面加载后有多少缓冲动作被触发;这可能表明它的构建方式有问题——我是否遗漏了什么?

听起来在这种情况下可能会很好地工作

将每个源值投影到一个可观察对象,该可观察对象以序列化方式合并到输出可观察对象中,等待每个源值在合并下一个源值之前完成

因此,在下面的例子中,一次只运行一个计时器。在前一个仍在等待时传入的任何ping都将被缓冲

const pingEpic = action$ =>
  action$.ofType(PING)
    .concatMap(() =>
      Observable.timer(1000)
        .mapTo({ type: PONG })
    );



请记住,大多数redux可观察问题可以重新定义为常规RxJS问题,从而拓宽了资源范围,帮助您找到答案。这就是redux的魅力所在:它几乎完全是规则的RxJS模式。

因此,虽然操作的输出并不完全令人满意(鉴于启动操作是由用户事件发出的,我对此无能为力),但Cartant推荐的建议实际上正是我所需要的

:

忽略由另一个可观察对象确定的持续时间内的源值,然后从源可观察对象发出最近的值,然后重复此过程

本质上,这允许我忽略多个发出的“PING”事件,而其中一个正在进行。然后它将继续执行最近的“PING”事件,因此我们看到的输出如下所示:

(click) PING (click) PING (click) PING (click) PING PONG DONE PONG DONE
第一个和最后一个“乒乓”动作是唯一通过Epic传播的动作,因此我们看到最后两个乒乓动作,都是在完成动作之后

因此,下面是一个已回答的示例(也可以在我的代码笔上看到)


听起来接线员对你很有用。@cartant谢谢,你的建议正是我所需要的!我要试试这个;但在我的特定用例中,“ping”表示一系列其他动作的开始,最后一个动作完成并发出“pong”动作。因此,在您的示例中,我可能会延迟它以开始下一个操作,但理想情况下,如果没有发生“乒乓”,我需要防止下一个“乒乓”。因此,输出应该是:(单击)ping(单击)(单击)(单击)pong ping(理想情况下,我希望进行过滤,以便只允许传播最近未执行的操作)澄清一下,您是说要防止/缓冲ping吗?这在redux observable(有意)中是不可能的,因为这意味着修改从UI发送的操作的意图。相反,使用intent动作来表示可能的其他动作的开始。如果说得更清楚,你可以将PING重命名为BUFFERED_PING之类的东西,那么epic可能会立即发出PING,或者等到收到PONG后再启动。这通常会让人们在相对不熟悉redux observable/redux传奇时感到困惑。请记住,从UI直接发送的操作应该始终是意图,这可能会或可能不会立即导致所需的副作用,具体取决于epics中所需的业务逻辑。
(rapidly click four times)
PING
PING
PING
PING
(1000ms pass)
PONG
(1000ms pass)
PONG
(1000ms pass)
PONG
(1000ms pass)
PONG
(click) PING (click) PING (click) PING (click) PING PONG DONE PONG DONE
const pingEpic = action$ =>
  action$.ofType(PING)
    .audit(() => actionPauser.filter(paused => !paused))
    .do(() => actionPauser.next(true))
    .delay(1000)
    .mapTo({ type: PONG });

const pauseEpic = action$ =>
  action$.ofType(PONG)
    .delay(1000)
    .mapTo({ type: DONE })
    .do(() => actionPauser.next(false));