Asherlc
Asherlc

Reputation: 1161

Blocking takeEvery with redux saga

I have two sets of actions that I would like to be able to call from different parts of my app via redux. However, I want to run them in the correct order, and make sure that set b is never run before set a.

For example, let's say I have two actions, configure and execute. I need to make sure that execute is never run before configure, but they may be dispatched out of order. In that case, I need to execute to be enqueued and only processed once configure has finished.

Is this possible to do in redux-saga?

I've tried the following, which doesn't seem to be blocking at all:

function* configureSaga({ payload }): IterableIterator<any> {
  yield put(aFirstThing(payload));
  yield put(aSecondThing());
}

function* executeSaga({ payload }): IterableIterator<any> {
  yield put(aThirdThing(payload));
  yield put(aFourthThing());
}

export function* adsSaga(): IterableIterator<any> {
  const configureChanel = yield actionChannel({ type: 'configure' });
  yield takeEvery(configureChanel, configureSaga);
  yield takeEvery({ type: 'execute' }), executeSaga);
}

Given two dispatches in the following order:

dispatch({ type: 'execute' })
dispatch({ type: 'configure' })

I need the aFirstThing, aSecondThing, aThirdThing, aFourthThing actions executed in that order.

Is such a thing possible with redux-sagas?

Upvotes: 1

Views: 1735

Answers (1)

Martin Kadlec
Martin Kadlec

Reputation: 4975

takeEvery is useful for the most common cases, but for more complex ones you will often end up writing a custom cycle instead. The implementation will differ based on how you want to deal with extra actions dispatched during configuration/execution.

Ignore all additional configure/execute actions (while waiting for the second action) + Ignore all actions until the configuration is done:

export function* adsSaga() {
  while (true) {
    const [executePayload, configurePayload] = yield all([
      take('configure'),
      take('execute'),
    ])
    yield call(configureSaga, configurePayload);
    yield fork(executeSaga, executePayload);
  }
}

Ignore all additional configure/execute actions (while waiting for the second action) + Buffer all actions until the configuration is done:

export function* rootSaga() {
  const configureChan = yield actionChannel('configure');
  const executeChan = yield actionChannel('execute');
  while (true) {
    const [executePayload, configurePayload] = yield all([
      take(configureChan),
      take(executeChan),
    ])
    yield call(configureSaga, configurePayload);
    yield fork(executeSaga, executePayload);
  }
}

To make the configure+execute actions to wait for each oher you can use the combination of all+take. You need the actionChannel only when you want to deal with multiple "rounds" of the configure+execute combo.

You can modify these solutions to e.g. buffer only the configure or execute action. Or if you want to ignore all actions until both configuration and execution is done use call instead of fork to run the executeSaga. You can also allow multiple configurations run at the same time by putting the call+fork effect inside another forked saga. There is also option to pass custom buffer to the action channel if you want to e.g. buffer max one action at a time.

Upvotes: 1

Related Questions