Search code examples
asynchronousreduxredux-sagayield-keyword

Chaining asynchronous actions using redux-sagas


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.


Solution

  • 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.