如何在没有 store.dispatch 的情况下链接异步操作并等待结果

How to chain async actions and wait for the result without store.dispatch

我正在尝试编写我的 INITIALIZE 操作,它应该按以下方式将一些异步操作链接在一起

  1. 调用初始化操作。
  2. 同时调用两个异步操作。
  3. 等待以上操作完成。
  4. 运行 加一动作。
  5. 完成初始化。

这是我期望的 redux 流程

INITIALIZATION_STARTED => ASYNC_ACTION_A_STARTED AND ASYNC_ACTION_B_STARTED => ASYNC_ACTION_A_FINISHED AND ASYNC_ACTION_B_FINISHED => ASYNC_ACTION_C_STARTED => ASYNC_ACTION_C_FINISHED => INITIALIZATION_FINISHED

我设法在我的史诗中使用 store.dispatch 实现了该流程,我知道这是 anti-pattern 并且它将在 1.0.0 版本中被删除所以我想知道我是如何做到的可以使用纯 epics

我的工作解决方案

export const initEpic = (action$: ActionsObservable<Action>, store) =>
  action$.filter(actions.initialization.started.match)
    .switchMap(action => (
      Observable.forkJoin(
        waitForActions(action$, actions.asyncA.done, actions.asyncB.done),
        Observable.of(
          store.dispatch(actions.asyncA.started(action.payload)),
          store.dispatch(actions.asyncB.started(action.payload)),
        )
      ).map(() => actions.asyncC.started(action.payload))
    )
  );

const waitForActions = (action$, ...reduxActions) => {
  const actionTypes = reduxActions.map(x => x.type);
  const obs = actionTypes.map(type => action$.ofType(type).take(1));
  return Observable.forkJoin(obs);
}

我也一直在尝试使用 forkEpic 从这个 comment 那样

export const initEpic = (action$: ActionsObservable<Action>, store) =>
  action$.filter(actions.initialization.started.match)).mergeMap(action =>
    forkEpic(loadTagsEpic, store, actions.asyncA.started(action.payload))
      .concat(
        forkEpic(loadBranchesEpic, store, actions.asyncB.started(action.payload))
      )
      .map(() => actions.asyncC.started(action.payload))
  );

但它不发送启动操作 ASYNC_ACTION_A_STARTED_ASYNC_ACTION_B_STARTED

听起来 merge 非常适合这个。您将开始监听 asyncA.doneasyncB.done,然后在等待期间通过发出 asyncA.startedasyncB.started 来启动请求。这两个流合并为一个流,因此它以正确的顺序发生,并且任何一个发出的动作都由我们的史诗发出,而不需要 store.dispatch.

const initEpic = action$ =>
  action$.filter(actions.initialization.started.match)
    .switchMap(action => (
      Observable.merge(
        waitForActions(action$, actions.asyncA.done, actions.asyncB.done)
          .map(() => actions.asyncC.started(action.payload)),
        Observable.of(
          actions.asyncA.started(action.payload),
          actions.asyncB.started(action.payload),
        )
      )
    )
  );

这是一个 JSBin 演示:https://jsbin.com/yonohop/edit?js,console

它不做任何 ASYNC_ACTION_C_FINISHEDINITIALIZATION_FINISHED 的事情,因为问题中没有包含相关的代码,所以不确定它会做什么。

您可能会注意到这主要是一个常规的 RxJS 问题,其中项目流 碰巧 是动作。这真的很有帮助,因为当你寻求帮助时,如果你将问题设计为通用 RxJS,你可以从整个 RxJS 社区询问。


请注意,我在开始之前听完了;如果 donestarted 之后同步发出,这通常是最佳实践。如果你不先听,你就会错过它。因为它是异步的所以没关系,但通常仍然是最佳实践并且在您进行单元测试时很有帮助。