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

廉价感情. 提交于 2019-12-23 04:47:49

问题


I'm trying to write my INITIALIZE action which should chain some async actions together in the following way

  1. Call the initialize action.
  2. Call two async actions simultaneously.
  3. Wait for the completion of above actions.
  4. Run additional one action.
  5. Finish initialization.

here is the redux flow that I expect

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

I managed to achieve that flow using store.dispatch inside my epic, I know that this is anti-pattern and it will be removed in the 1.0.0 version so I would like to know how I can do it using pure epics

My working solution

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);
}

I have also been trying to use forkEpic from this comment like that

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))
  );

but it doesn't dispatch starting actions ASYNC_ACTION_A_STARTED and _ASYNC_ACTION_B_STARTED


回答1:


Sounds like merge is perfect for this. You'll start listening for asyncA.done and asyncB.done and then while waiting you'll kick off the requests by emitting asyncA.started and asyncB.started. These two streams are merged together as one, so it happens in the correct order and the actions emitted by either are emitted by our epic without needing 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),
        )
      )
    )
  );

Here is a JSBin demoing: https://jsbin.com/yonohop/edit?js,console

It doesn't do any of the ASYNC_ACTION_C_FINISHED and INITIALIZATION_FINISHED stuff because code for that was not included in the question so not sure what it would have done. 😁

You might notice this is mostly a regular RxJS question where the items streaming happen to be actions. This is really helpful because when you ask for help you can ask from the entire RxJS community if you craft the question as generic RxJS.


Note that I listened for done before starting; this is generally a best practice in case done is emitted synchronously after started. If you didn't listen first, you'd miss it. Since it's async it doesn't matter, but still generally a best practice and helpful when you unit test.



来源:https://stackoverflow.com/questions/47000384/how-to-chain-async-actions-and-wait-for-the-result-without-store-dispatch

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!