Reputation: 1193
I have issues with yield all in saga effect, I provide my sample code below
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" }
})
);
}
}
The logic of it is that I want to call multiple requests (some success, and some failed).
Expected: If it has failed request, the error will be caught after yield all but those success requests still continue and it should dispatch action "fetchDataSuccess" after individual success request (Promise.all can do this)
Actual: If it has failed request, the error will be caught after yield all, and then saga immediately cancel all other "fetchData" call.
Can anyone help me to achieve this logic. Thanks in advance.
Upvotes: 1
Views: 2940
Reputation: 42170
The "Actual" behavior that you are describing fits with what I am seeing in your code. As soon as any error is thrown, we leave the try
block and enter the catch
block.
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 howPromise.all
behaves). - docs
If you want each fetch
to execute then you would need to put the try
/catch
inside the .map
. You can either map to an array of true
/false
values or set a value on error. Or if you don't mind having multiple snackbars you could put
enqueueSnackbar
inside fetchData
instead of in fetchSummary
.
Here's one way to do it:
// 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" }
})
);
}
}
Upvotes: 3