redux saga дает все отменить другой эффект при сбое одного

#javascript #reactjs #redux #redux-saga

#javascript #reactjs #redux #redux-saga

Вопрос:

У меня проблемы с выходом всего в эффекте saga, я предоставляю свой пример кода ниже

 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" }
      })
    );
  }
}
 

Логика этого заключается в том, что я хочу вызвать несколько запросов (некоторые успешные, а некоторые неудачные).

Ожидается: если запрос не удался, ошибка будет перехвачена после получения всех, но эти успешные запросы все еще продолжаются, и он должен отправить действие «fetchDataSuccess» после отдельного успешного запроса (Promise.all может это сделать)

Актуально: если запрос не удался, ошибка будет перехвачена после выхода all, а затем saga немедленно отменит все остальные вызовы «fetchData».

Кто-нибудь может помочь мне достичь этой логики. Заранее спасибо.

Ответ №1:

«Фактическое» поведение, которое вы описываете, соответствует тому, что я вижу в вашем коде. Как только выдается какая-либо ошибка, мы покидаем try блок и входим в catch блок.

Когда мы yield создаем массив эффектов, генератор блокируется до тех пор, пока все эффекты не будут разрешены или как только один из них будет отклонен (точно так же, как Promise.all ведет себя). — документы

Если вы хотите, чтобы каждый fetch из них выполнялся, вам нужно будет поместить try / catch внутри .map . Вы можете либо сопоставить с массивом true / false values, либо установить значение для ошибки. Или, если вы не возражаете против нескольких закусочных, вы могли put enqueueSnackbar бы использовать их внутри 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

Комментарии:

1. Да, это очень похоже на Promise. все, как обещание успеха, все еще выполняется, даже если другое не удалось. Единственное отличие заключается в том, что мы можем отправлять сообщение только тогда, когда все обещания выполнены, для Promise.all блок catch будет запущен сразу после того, как была выдана ошибка, в то время как другие обещания, не содержащие ошибок, все еще выполняются