RXJS repeat没有机会重复吗?

RXJS repeat没有机会重复吗?,rxjs,rxjs5,redux-observable,Rxjs,Rxjs5,Redux Observable,我在应用程序中使用以下epic来处理api请求: action$ => { return action$.ofType(actions.requestType) .do(() => console.log('handled epic ' + actions.requestType)) .switchMap((action) => ( Observable.create((obs) => { obs.next({ type:

我在应用程序中使用以下epic来处理api请求:

action$ => {
  return action$.ofType(actions.requestType)
    .do(() => console.log('handled epic ' + actions.requestType))
    .switchMap((action) => (
      Observable.create((obs) => {
        obs.next({ type: type, value: action.value, form: action.form });
      })
      .debounceTime(250)
      .switchMap((iea) => (
        Observable.ajax(ajaxPost(url(iea.value), body ? body(iea.value) : action.form))
          .mergeMap(payload => {
            return Observable.merge(
              Observable.of(actions.success(payload)),
              /* some other stuff */
            );
          })
          .catch(payload => {
            return [actions.failure(payload)];
          })
      ))
  ))
  .takeUntil(action$.filter((a) => (a.type === masterCancelAction))
  .repeat();
};
基本上,每当我执行api请求时,我都会发送一个请求操作。如果我快速发送另一个请求,则使用debounceTime忽略前一个请求。此外,可以使用masterCancelAction取消请求,取消后,repeat()将重新启动epic。这部史诗在任何情况下都能如期发挥作用

当用户在请求期间重新使用浏览器时,会发生故障情况。在本例中,我对请求启动masterCancelAction。但是,在masterCancelAction产生的同一执行上下文上,另一个请求操作调度以在同一epic上执行新请求,但api请求不会发生(尽管console.log会发生),就好像没有重复()。在发生取消的其他情况下,下一个请求不会从相同的执行上下文调用,并且工作正常,因此在这种情况下,我的代码似乎没有给repeat重新启动epic的机会

我发现一个糟糕的解决方法是对取消后发送的请求使用setTimeout(dispatch(action),0)。这似乎允许repeat()执行。我尝试将不同的调度程序传递到repeat中,但这似乎没有帮助。另外,将takeUntil和repeat附加到我的内部switchMap中解决了这个问题,但是我的下一个请求没有在同一个调用堆栈中执行的其他情况也会失败

有没有一种不用setTimeout就能解决这个问题的方法?也许这不是一个重复相关的问题,但似乎是这样


使用rxjs 5.0.3和redux observable 0.14.1.

如果没有类似jsbin的东西来理解您的意思,问题并非100%清楚,但我确实看到了一些可能有帮助的一般问题:

匿名观察永远不会完成 创建自定义匿名观察对象时,如果确实希望它完成,则调用
observer.complete()
非常重要。在大多数情况下,不这样做会导致订阅内存泄漏,还可能导致其他奇怪的行为

Observable.create((observer) => {
  observer.next({ type: type, value: action.value, form: action.form });
  observer.complete();
})
可观察到的
将是等效的:

Observable.of({ type: type, value: action.value, form: action.form })
但是,不清楚为什么会这样做,因为它发出的值在范围内捕获

debounceTime
在这种情况下,它不会去抖动,而是会延迟 由于它应用于的匿名可观测对象只会发出单个项目,
debounceTime
将作为常规的
.delay(250)
。我敢打赌,您打算对
actions.requestType
actions进行去抖动,在这种情况下,您需要在类型(actions.requestType)的
action$之后,在
switchMap
之外应用去抖动

Observable.of
接受任何数量的参数 这更多的是一个“你知道吗?”而不是一个问题,但我注意到你正在将你的
/*一些其他动作*/
合并在一起,我认为这是其他的
观测值。相反,您可以只返回一个
,并将操作作为参数传递

Observable.of(
  actions.success(payload),
  /* some other actions */
  actions.someOtherOne(),
  actions.etc()
);

此外,当你发现自己像这样同步地发出多个动作时,考虑减缩器是否应该监听相同的单动作而不是拥有两个或更多个动作。有时这是没有意义的,因为你希望它们有完全不相关的动作,只是要记住人们经常忘记的一点——所有的还原程序都接收所有的动作,因此多个还原程序可以从同一个动作改变它们的状态

.takeUntil
将阻止epic监听未来的操作 将
takeUntil
放在顶级可观察链上会导致epic停止侦听类型(actions.requestType)
action$,这就是为什么在之后添加了
.repeat()
。这在某些情况下可能有效,但它效率低下,并可能导致其他难以实现的错误。Epics应该被看作是一种类似于sidecar的过程,通常使用应用程序“启动”,然后继续监听特定的操作,直到应用程序“关闭”,即用户离开应用程序。它们实际上不是过程,从概念上把它们看作一种抽象是很有帮助的

因此,每次它匹配它的特定动作时,它通常会
切换map
合并map
concatMap
,或者
耗尽map
,产生一些副作用,比如ajax调用。那个内部可观察的链条就是你们想要取消的。因此,您可以将
.takeUntil
放在链中的适当位置


总结 如前所述,如果没有一个更完整的示例(如jsbin),您打算做什么以及问题是什么还不清楚。但严格根据提供的代码,这是我的猜测:

const someRequestEpic = action$ => {
  return action$.ofType(actions.requestType)
    .debounceTime(250)
    .do(() => console.log('handled epic ' + actions.requestType))
    .switchMap((action) =>
      Observable.ajax(ajaxPost(url(action.value), body ? body(action.value) : action.form))
        .takeUntil(action$.ofType(masterCancelAction))
        .mergeMap(payload => {
          return Observable.of(
            actions.success(payload),
            /* some other actions */
            ...etc
          );
        })
        .catch(payload => Observable.of(
          actions.failure(payload)
        ))
    );
};
查看redux可观察文档中的页面


如果这有点让人困惑,我建议你更深入地研究什么是可观测的,什么是“操作符”以及“操作符”是什么,这样它就不会让人觉得不可思议,你应该把操作符放在哪里才更有意义


本在上的帖子是一个好的开始。

谢谢你的帮助!你说得对,史诗很奇怪。它一开始是一部史诗般的作品,与你的建议相似,但TakeTill的位置不正确,导致了大量的边缘案例bug和比赛条件,最终演变成了一团混乱。我切换到你建议的史诗,它似乎工作得很好。