Reputation: 10686
I have a scenario where I need to make a number of api calls in parallel. For now, the code uses redux-saga all
to do this:
try {
const results = yield all(
someIds.map(id =>
call(axios.delete, `/api/somewhere/items/${id}`)
)
);
console.log(results);
yield put(someSuccessFunction)
} catch(e) {
yield put(someFailureFunction)
}
In the event that all calls are successful, results
logs properly as an array of axios responses, with headers
, request
, status
, etc. However, if even a single call fails, the code jumps to the catch
block. I have no way of knowing which call failed.
I have read How to handle array of requests in redux saga, but in the examples there, it seems the accepted answer is tracking success or failure on a per-call basis. I need to know if all calls succeeded, and if so, dispatch a success action. If any calls failed, I need to know which ones failed, and dispatch a failure or partial failure action. It might look like this:
try {
const results = yield all(
someIds.map(id =>
call(axios.delete, `/api/somewhere/items/${id}`)
)
);
const success = results.every(result => result.status === 200);
const failure = results.every(result => result.status !== 200);
const partialFailure =
results.some(result => result.status === 200) &&
results.some(result => result.status !== 200);
if (success) put(someSuccessAction);
if (failure) put(someFailureAction);
if (partialFailure) put(somePartialFailureAction);
} catch(e) {
yield put(someFailureFunction);
}
But I can't seem to grasp how to retrieve the array of results when any 500
response skips us into the catch block. What is the best tactic to do this?
Upvotes: 0
Views: 1252
Reputation: 4975
The trick is to have a separate try..catch for each request and map the successful or failed results into a structure on top of which you can then run various array operations. Essentially, it works like Promise.reflect. In this case, it could look something like this:
function* deleteById(id) {
try {
const result = yield call(axios.delete, `/api/somewhere/items/${id}`)
if (result.status !== 200) throw new Error('Invalid status')
return {v: result, status: 'fulfilled'}
} catch (err) {
return {e: err, status: 'rejected'}
}
}
function* deleteSaga() {
const results = yield all(
someIds.map(id => call(deleteById, id))
)
const success = results.every(result => result.status === 'fulfilled')
const failure = results.every(result => result.status === 'rejected')
//...
}
I've separated the request by id into its own saga for readability, though it can be inline generator as well.
Upvotes: 2