当一个失败时,redux saga yield all cancel other effect

redux saga yield all cancel other effect when one failed

我在 saga 效果中遇到 yield all 问题,我在下面提供了我的示例代码

function* fetchData(item) {
  try {
    const data = yield call(request, url);
    yield put(fetchDataSuccess(data));
  } catch (error) {
    yield put(fetchDataFailure(error));
    throw error;
  }
}

function* fetchSummary(action) {
  try {
      yield all(
        list.map(item=>
          call(fetchData, item)
        )
      );
  } catch (error) {
    yield put(
      enqueueSnackbar({
        message: "Has Error",
        options: { variant: "error" }
      })
    );
  }
}

它的逻辑是我要调用多个请求(有的成功,有的失败)。

预期:如果它有失败的请求,错误将在 yield all 之后被捕获,但那些成功的请求仍然继续并且它应该在个别成功请求后调度操作“fetchDataSuccess”(Promise.all 可以做到这一点)

实际:如果请求失败,yield all 后会捕获错误,然后 saga 立即取消所有其他“fetchData”调用。

谁能帮我实现这个逻辑。提前致谢。

您所描述的“实际”行为符合我在您的代码中看到的情况。一旦抛出任何错误,我们就离开 try 块并进入 catch 块。

When we yield an array of effects, the generator is blocked until all the effects are resolved or as soon as one is rejected (just like how Promise.all behaves). - docs

如果您希望每个 fetch 都执行,那么您需要将 try/catch 放在 .map.您可以映射到 true/false 值的数组或设置错误值。或者如果你不介意有多个小吃店,你可以 put enqueueSnackbar inside fetchData 而不是 fetchSummary.

这是一种方法:

// modified to return either true or false
function* fetchData(item) {
  try {
    const data = yield call(request, item);
    yield put(fetchDataSuccess({ item, data }));
    return true;
  } catch (error) {
    yield put(fetchDataFailure({ item, error }));
    return false;
  }
}

function* fetchSummary(action) {
  const results = yield all(
    action.payload.list.map((item) => call(fetchData, item))
  );
  // check if any of the results were false;
  const hasError = results.some((res) => !res);
  if (hasError) {
    yield put(
      enqueueSnackbar({
        message: "Has Error",
        options: { variant: "error" }
      })
    );
  }
}

Code Sandbox Demo