I'm trying to use retryWhen
in HTTP calls.
It works perfectly when try to use like this:
return this.http.get(`${environment.apiUrl}/track/${this.user.instance._id}/${this.currentPlayer.playlist.id}/next?s=${this.playerCounter}`, options)
.timeout(500, new TimeoutError(`Timeout trying to get next track. [instanceId=${this.user.instance._id}]`))
.retryWhen(attempts => {
return Observable.range(1, 3).zip(attempts, i => i).flatMap(i => 3 === i ? Observable.throw(attempts) : Observable.timer(i * 1000));
})
It makes a maximum of 3 tries if get a Timeout error.
But, always have a buuut, I want to make this more abstract to use on various use cases and for this, I have to check the type of the error.
Only TechnicalErros will be retried.
So I tried this without success.
.retryWhen(attempts => {
return attempts.flatMap(error => {
if(error instanceof TechnicalError) {
return Observable.range(1, 3).zip(attempts, i => i).flatMap(i => 3 === i ? Observable.throw(attempts) : Observable.timer(i * 1000));
} else {
Observable.throw(error);
}
});
})
It stops at first try and does not execute the Observable.timer()
, neither the Observable.throw()
.
I have almost sure that the problem is about the first flatMap
, I already tried to use mergeMap
, without success.
Thanks in advance!
In RxJS 5 flatMap()
is just alias to mergeMap()
:).
The problem is in the way you use the callback for retryWhen()
operator. It's called just once and then every time an error signal arrives it's pushed to the Observable returned from this callback.
In your second example you're returning Observable from attempts.flatMap
and then subscribing to it again that callback with .zip(attempts, i => i)
. But this zip
operator is never called because it's called after the value has been already consumed by attempts.flatMap
. Also this is why the Observable.range(1, 3)
starts always from the beginning.
I know this looks confusing. Just be aware thet:
retryWhen()
is called just once.attempts.flatMap()
is called every time an error arrives.So you just need to restructure your code, for example like the following:
var source = Observable.create(obs => {
obs.next(1);
obs.next(2);
obs.error(new TechnicalError('error from source'));
})
.retryWhen(attempts => {
console.log('retryWhen callback');
let count = 0;
return attempts.flatMap(error => {
if (error instanceof TechnicalError) {
console.log(error);
return ++count >= 3 ? Observable.throw(error) : Observable.timer(count * 1000);
} else {
return Observable.throw(error);
}
});
})
.subscribe(
val => console.log(val),
err => console.log('subscribe error', err),
_ => console.log('complete')
);
This prints to console:
1
2
retryWhen callback
TechnicalError { msg: 'error from source' }
1
2
TechnicalError { msg: 'error from source' }
1
2
TechnicalError { msg: 'error from source' }
subscribe error TechnicalError { msg: 'error from source' }
See live demo: https://jsbin.com/hobeda/3/edit?js,console
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With