Reputation: 1874
I'd like to handle ajax timeouts using redux-observable so that if a timeout occurs (after say 10 seconds) it will retry the request another two times (firing a SAVE_RETRYING
action every time so the UI can notify the user that it's retrying).
For any other type of error or if we've already retried twice it should just fail and fire a SAVE_FAILURE
action.
I can make it work if I trigger the SAVE_RETRYING
action using store.dispatch
but getting deprecation warnings about this and I'm a bit stuck figuring out how to do it the proper way (adding SAVE_RETRYING
to the stream that is returned by the epic).
Here's what I have (simplified):
function saveEpic(action$, store) {
return action$.ofType('SAVE_CLICKED')
.mergeMap(action => (
ajax({
url: '/a-long-request',
})
.timeout(10000)
.map(() => ({ type: 'SAVE_SUCCESS' }))
.retryWhen(errors => (
errors.scan((count, e) => {
if (count >= 2 || e.name !== 'TimeoutError') {
throw e;
} else {
store.dispatch({ type: 'SAVE_RETRYING', count });
return count + 1;
}
}, 0)))
.startWith({ type: 'SAVE_STARTED' })
.catch(() =>
Observable.of({ type: 'SAVE_FAILURE' }))
));
}
How can I get that SAVE_RETRYING
action up to the main stream? Thx.
Upvotes: 0
Views: 1150
Reputation: 3435
This is not ideal, but you could use catch
and undocumented second argument (which is the source observable) to resubscribe. The downside I don't like is you have to count retries in the mergeMap
callback closure.
function saveEpic(action$, store) {
return action$.ofType('SAVE_CLICKED')
.mergeMap(action => {
let retries = 0;
return ajax({
url: '/a-long-request',
})
.timeout(10000)
.map(() => ({ type: 'SAVE_SUCCESS' }))
.catch((error, source) => {
retries += 1;
if (retries >= 2 || error.name !== 'TimeoutError') {
return Observable.of({ type: 'SAVE_FAILURE' });
}
return source.startWith({ type: 'SAVE_RETRYING', count: retries });
})
.startWith({ type: 'SAVE_STARTED' });
});
}
Upvotes: 2