Search code examples
reactjsreduxreact-reduxes6-promiseredux-saga

How to deal with errors using all effect in redux-saga


I'm using redux-saga to start multiple requests concurrently as described in the redux-saga docs. The all effect has an all or nothing semantics, similar to Promise.all.

Only if all effects succeed, yield all([...]) succeeds. However, I am doing several requests from which I expect some of them to fail and some of them to succeed. I would like to start all of them in parallel and consume the responses from those requests that have succeeded.

Therefore, I tried to wrap the request into a Promise that always resolves no matter whether the request was successful or not:

// watcher saga
export function* watchMultipleRequests() {
  while(true) {
    const {ids} = yield take('VIDEOS_REQUEST');
    yield fork(doMultipleRequests, ids);
  }
}

// worker saga
export function* doMultipleRequests(ids) {
  const requests = ids.map(id => {
    // api.buildVideoRequest returns a promise once it is invoked
    const wrapper = ignoreErrors(api.buildVideoRequest, id);
    return call(wrapper);
  });
  try {
    const responses = yield all(requests);
    yield put({type: 'VIDEOS_SUCCESS', responses});
  } catch (error) {
    // should not happen because we are always resolving the promise
    console.log(error);
  }
};

export function ignoreErrors(fn, ...args) {
  return function* () {
    yield new Promise(function (resolve) {
      return fn(...args)
        .then(response => {
          console.log('success = ', response);
          resolve(response);
        })
        .catch(response => {
          console.log('error = ', response);
          resolve(response);
        });
    });
  }
}

I would like to handle the error cases in the reducer. However, if I fire n requests, the responses array contains n times undefined. Has anyone a clue on why this is not working?


Solution

  • The issue is that the ignoreErros function is a generator function. Implementing it like this:

    export function ignoreErrors(fn, ...args) {
      return () => {
        const ignoreErrorCallback = (response) => response;
        return fn(...args).then(ignoreErrorCallback, ignoreErrorCallback);
      };
    }
    

    is sufficient.