如何等待其他史诗完成,然后调用回调?

时间:2019-04-25 14:05:45

标签: redux-observable

我试图在第一个史诗中映射多个动作,在第二个史诗中处理它们,然后在第一个史诗中调用回调。

这是我看到的流程:

  1. fetchMultipleOperationsEpic监听OPERATIONS_FETCH_ALL和 创建fetchOperations操作流。
  2. 然后fetchOperationsEpic启动,使用 每个已分派动作的fetchOperationData。
  3. 获取每个操作的数据后, fetchMultipleOperationsEpic应该调用回调。
const fetchMultipleOperationsEpic = action$ => action$.pipe(
    ofType(OPERATIONS_FETCH_ALL),
    switchMap(({ payload }) => {
        const stream = of(...payload.ids.map(processId => fetchOperations({ processId })));

        // PROBLEM HERE: 'done' is logged before actions from fetchOperationsEpic complete
        return stream.pipe(finalize(() => console.log('done')))
    })
)

const fetchOperationsEpic = action$ => action$.pipe(
    ofType(OPERATIONS_FETCH),
    mergeMap(({ payload }) => fetchOperationData(payload.processId))
)

const fetchOperationData = processId => {
    api.fetchOperationsByProcess(processId).pipe(
        mergeMap(operations => of(fetchSuccess(), fooAction(), barAction())),
        catchError(error => of(fetchOperationsFailure({ processId, error })))
    )
}

也尝试使用forkJoin失败,

const fetchMultipleOperationsEpic = action$ => action$.pipe(
    ofType(OPERATIONS_FETCH_ALL),
    switchMap(({ payload }) => {
        const observables = [...payload.ids.map(processId => of(fetchOperations({ processId })))];

        // Doesn't wait as well
        return forkJoin(observables).pipe(
            mergeMap(o => o),
            finalize(() => console.log('done'))
        )
    })
)

事实上,使它起作用的唯一方法是避免调用fetchOperations操作(从而避免调用fetchOperationsEpic),而直接调用fetchOperationData:

const fetchMultipleOperationsEpic = action$ => action$.pipe(
    ofType(OPERATIONS_FETCH_ALL),
    mergeMap(({ payload }) => {
        const observables = [...payload.ids.map(id => fetchOperationData(id))];
        return merge(...observables)
            .pipe(finalize(() => console.log('done')))
    })
)

哪个好。我只是想知道我最初的方法是否确实可行。

1 个答案:

答案 0 :(得分:0)

在您认为“这里有问题”的地方:

return stream.pipe(finalize(() => console.log('done')))

实际上是正确的。 finalize将在紧随其后运行 由其上边的of返回的动作列表 。那是因为您已将finalize明确地链接到该内部可观察对象。

您正在考虑的方法的问题在于,一旦动作执行到存储中,就不再属于该特定可观察的范围。您不能简单地等待其他史诗中特定动作的处理结束,因为最终,史诗永远不会真正结束。从严格的RxJS角度讲,可观察对象永远不会发出完整通知

通常,我建议您研究使用单个史诗的解决方案(也许遵循您上一个示例的内容)。这样一来,您就可以简单地将诸如finalize之类的运算符链接起来,而无需跳过太多的麻烦。您仍然可以重构代码,以便在希望在不同的史诗之间共享/复制业务逻辑时,史诗可以使用可重用的功能。

您还可以研究一种依赖于通过不同动作进行史诗间交流的解决方案。但是,这可能会变得棘手,具体取决于您的工作流程的复杂性。您需要执行每个操作才能唯一标识子流程。这是一个主意:

const fetchMultipleOperationsEpic = action$ => action$.pipe(
  ofType(OPERATIONS_FETCH_ALL),
  switchMap(({ payload }) =>
    merge(...payload.ids.map(processId =>
      concat(
        of(fetchOperations({ processId })),
        // Note: This keeps the observable running until either
        // the corresponding "success" or "failure" action is
        // dispatched:
        action$.pipe(
          filter(action => (action.type === FETCH_SUCCESS || action.type === FETCH_FAILURE) && action.payload.processId === processId),
          first(),
          ignoreElements(),
        ),
      )
    )).pipe(
      finalize(() => console.log('done')),
    )
  )
)

const fetchOperationsEpic = action$ => action$.pipe(
    ofType(OPERATIONS_FETCH),
    mergeMap(({ payload }) => fetchOperationData(payload.processId))
)

const fetchOperationData = processId => {
    api.fetchOperationsByProcess(processId).pipe(
        // Note: We have to pass along the `processId` in the
        // "success" action so that the inner observables in
        // `fetchMultipleOperationsEpic` can complete:
        mergeMap(operations => of(fetchSuccess({ processId }), fooAction(), barAction())),
        catchError(error => of(fetchOperationsFailure({ processId, error })))
    )
}