Search code examples
redux-saga

Redux Saga not being cancelled


I have this type of composition of my sagas - a race between:

  • some long polling fetching
  • cancellation action that's triggered manually by the user
export function* doPollingSaga() {  
  try {
    while (true) {
      const res = yield call(fetchData, {})
      if(res) {
        yield put(saveResultsActionCreator(res))
        if(weHaveEverythingWeWanted) {
          break;
        }
      }
      yield delay(POLLING_DELAY);
    }
  } catch (e) {
    yield put(doSomeErrorReportingAndFinishActionCreator());
  } finally {
    if (yield cancelled()) {
      yield put(doSomePostCancelActionCreator());
    }
  }
}

export default function*() {
  yield race([
    take(ActionTypes.CANCEL),
    all([
      takeLatest(ActionTypes.DO_POLLING, doPollingSaga),
      // some more other sagas here
    ]),
  ]);
}

but when I dispatch ActionTypes.CANCEL from one of the UI components - nothing happens

In redux devtools I see that it's being dispatched correctly, but polling doesn't stop, doPollingSaga continues like nothing happened.

Inside doPollingSaga yield cancelled() === false all the time.

I've also tried replacing while(true) with while(!(yield cancelled())) but no effect as well.

From the docs I assume that everything inside all([...]) should be cancelled when ActionType.CANCEL wins the race

Not sure if I'm missing something important about cancellation of sagas.


Solution

  • Looks like the cause is in nesting order of race and takeLatest. The latter forks given saga, making it cancel-proof. You have to either listen to cancel actions inside the polling loop, or make a race inside the takeLatest:

    function* cancellableSaga() {
      yield race([take('cancel_polling'), call(doPollingSaga)]);
    }
    

    The whole example