在第一个请求完成之前忽略操作的史诗

Epic that ignores action until first request completes

我和一位同事向我们的 android 应用程序添加了一个类似于 redux-observable 的流程,但无法弄清楚如何创建一个在第一次调用完成之前停止接收操作的史诗。

最初我认为我们可以使用 skipUntil() 但后来意识到 skipUntil() 的结果被吞没了并且永远不会传递给链的其余部分。

下面是一些粗略的 JS 代码,展示了我们希望实现的目标。

  const fetchUserEpic = action$ =>
  action$.ofType(FETCH_USER)
    .skipUntil( /* first request completes */ )
    .mergeMap(action =>
      ajax.getJSON(`/api/users/${action.payload}`)
        .map(response => fetchUserFulfilled(response))
        .takeUntil(action$.ofType(FETCH_USER_CANCELLED))
    );

这几乎就像我需要一个 skipMap() 操作符,它的行为类似于 switchMap() 但一次只接受一个请求,而如果观察正在进行则忽略所有项目。

感谢您的任何建议。

假设您非常确定这就是您所需要的(通常不是,在执行之前的查询时删除输入意味着结果已过时,switchMap and/or debounce通常是首选),take(1) + repeat 应该可以工作,因为可观察到的动作很热。

const fetchUserEpic = action$ =>
  action$.ofType(FETCH_USER)
    .take(1)
    .concatMap(action =>
      ajax.getJSON(`/api/users/${action.payload}`)
        .map(response => fetchUserFulfilled(response))
        .takeUntil(action$.ofType(FETCH_USER_CANCELLED))
    )
    .repeat()

这是模拟逻辑的片段。

// simulate action$.ofType(FETCH_USER) every second
const obs1 = Rx.Observable.interval(1000).publish()
obs1.connect()

function getStuff(i) {
  return Rx.Observable.timer(i*1000).mapTo(i)
}

obs1.take(1).concatMap(getStuff).repeat().subscribe(console.log)
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.0.2/Rx.js"></script>

您不需要做任何花哨的事情,已经有一个名为 exhaustMap 的运算符。 exhaustMapconcatMap 类似,不同之处在于它会在前一个仍在处理时静默丢弃传入的请求。

const fetchUserEpic = action$ =>
  action$.ofType(FETCH_USER)
    .exhaustMap(action =>
      ajax.getJSON(`/api/users/${action.payload}`)
        .map(response => fetchUserFulfilled(response))
        .takeUntil(action$.ofType(FETCH_USER_CANCELLED))
    );