8

I have an action called initialiseApp which, obviously, initialises the application. There are some things I need from the server, such as the user information, the styles and some details about the page that is being displayed (it is a single page application). I am using redux-sagas and I can't figure out a way to chain asynchronous actions so that they occur IN ORDER, not parallel.

I need the user information to be called BEFORE requesting any styling information, because on the server side, the call for the user goes and retrieves the user and sets up some things in the session. Because the styles are being requested in parallel at the moment, it is showing errors because the user has not been set up yet on the server.

So I've tried using put.sync but that doesn't seem to work, but here is my code so far (I'm also using TypeScript by the way):

private *invokeAndWaitOn(action: IAction<any>) {
    const putSync = (put as any).sync; // The typings file for redux-sagas does not yet include put.sync
    yield putSync(action);      
}

private *invokeArrayOfActions(actions: IAction<any>[]) {
    for (let action of actions) {
        yield* this.invokeAndWaitOn(action);
    }
}

but this doesn't seem to work and I can't find any examples of how to properly use redux-saga's put.sync effect.

Can anyone see what I am doing wrong or is there a better/correct way to do this please?

Thanks in advance.

bibi
  • 3,671
  • 5
  • 34
  • 50
Anupheaus
  • 3,383
  • 2
  • 24
  • 30

1 Answers1

10

Let's assume, for sake of this use-case, you have two actions fetchUser and fetchStyle. You want to fire the second action right after the first action succeeded.

In this case you need to have two other actions for each to handle Success and Error so that your actions be as follow:

For user fetching: fetchUser, fetchUserSuccess, and fetchUserError

For style fetching: fetchStyle, fetchStyleSuccess, and fetchStyleError

then wrap your the whole fetching process in, let's say sagaFunction as follow:

private* fetchUserSaga() {
  try {
    yield put(fetchUser());
    let response = yield call(/* fetchUser API function */])
    yield put(fetchUserSuccess(response));
  } catch (err) {
    yield put(fetchUserFailure(err));
  }
}

Hint: You gotta use call effect from redux-saga.

private* fetchStyleSaga() {
  try {
    yield put(fetchStyle());
    let response = yield call(/* fetchStyle API function */])
    yield put(fetchStyleSuccess(response));
  } catch (err) {
    yield put(fetchStyleFailure(err));
  }
}

Then pass those sagas functions to your invoker generator function, as follow:

/*
const sagas = [
  fetchUserSaga,
  fetchStyleSaga,
]; */

private* invokeArrayOfSagas(sagas: ISaga<any>[]) {
  for (let saga of sagas) {
    yield call(saga()); /* saga: fetchUserSaga || fetchStyleSaga */
  }
}

This will wait until the fetchUserSaga is completed then start fetchStyleSaga.

More info: If you want to group some actions and call them in parallel the another bunch of actions depends on them, you can yield them in an array with one yield keyword, see more here.

Basim Hennawi
  • 2,651
  • 3
  • 19
  • 31
  • Thanks for your reply, it is very comprehensive and does in fact answer the question. However, I have neglected to say (completely my fault) that I do not have direct access to the saga's themselves (just the way I have implemented it) and I only have access to the actions. I can call the actions, but not the actual sagas. I could re-work it to access the saga's directly, but it would require some heavy rework, if there was a way to use put or put.sync, that would be better for my current situation? – Anupheaus Dec 22 '16 at 22:42
  • Well, actually as far as you put the keyword `yield` before `put` in a generator so it's already synchronous, in either ways you need another actions for handling success so you can listen on success in your case. – Basim Hennawi Dec 22 '16 at 23:51
  • the link is dead https://yelouafi.github.io/redux-saga/docs/advanced/RunningTasksInParallel.html (404 error) – naXa stands with Ukraine Apr 21 '20 at 13:40
  • @BasimHennawi `You want to fire the second action right after the first action succeeded.` your implementation in the answer will fire the second action after the first also when the first **failed**. In thunk one can do `thunkOne(args)(dispatch).then(result=>thunkTwo(dispatch)(result)` but with sagas there is no easy way of doing so. – HMR Nov 02 '20 at 17:02