Search code examples
reactjsreact-reduxredux-saga

Confusion with dispatching an action in loop and tracking progress individually


I am dispatching an action let's say "GET_STATUS" in a loop for X number of time from a component. In saga file I have

function* actionWatcher() {
    yield all([
        takeLatest(Actions.GET_LATEST, getLatest),
    ]);
}

Inside getLatest* function there is this API call

//Some code
const results = yield call(api, {params});
//code after
callback()

I can clearly see API being called X number of time in network and also in chrome debugger I can see //Some code is executed X number of time. But //code after is executed only once in the end and callback function is being called just once in the end. I am expecting to be called for each occurrence.


Solution

  • If multiple Actions.GET_LATEST happen in rapid succession, then takeLatest is designed to cancel the old saga, and start a new one. If the saga is canceled while it's executing const results = yield call(api, {params});, that means it will never get to callback()

    If you don't want them to be canceled, then use takeEvery instead of takeLatest

    function* actionWatcher() {
        yield all([
            takeEvery(Actions.GET_LATEST, getLatest),
        ]);
    }
    

    If you want to keep the cancellation, but you need the callback to be called even if it's cancelled, you can use a try/finally:

    function* getLatest() {
      try {
        const results = yield call(api, {params});
      } finally {
        // This code will run whether it completes successfully, or throws an error, or is cancelled
        callback();
      }
    }
    

    If you need to specifically check whether it was cancelled in order to perform custom logic, you can do so with an if (yield cancelled()) in the finally block:

    function* getLatest() {
      try {
        const results = yield call(api, {params});
        callback(); // This line will only run if it's not cancelled and does not throw
      } finally {
        if (yield cancelled()) {
          // This line will only run if cancelled
        }
      }
    }