RXJS retryWhen with Promise
retryWhen(
receives: (errors: Observable) => Observable,
the: scheduler
): Observable
Задача: Повторить запрос к серверу, если запрос завершился с ошибкой. При повторении ошибки - пауза между запросами увеличивается, при достижении лимита повторов - запросы прекращаются.
retryWhen
- повторить наблюдаемую последовательность при возникновении ошибки, используя настраиваемый критерий.
import {timer, interval} from 'rxjs'
import {map, tap, retryWhen, delayWhen} from 'rxjs/operators'
// испускать значения каждую 1s
const source = interval(1000)
//
const example = source.pipe(
map(_ => {
if (_ > 5) {
// вызываем исключение, которое
// будет получено оператором retryWhen
throw _
}
return _
}),
// перехват ошибки, пауза, повтор последовательности
retryWhen(errors => errors.pipe(
// вывод сообщения (tap не влияет на поток)
tap(_ => console.log(`value ${_} was too high!`)),
// пауза
delayWhen(_ => timer(_ * 1000))
))
)
//
const sub = example.subscribe(console.log)
При ошибке поток будет повторен, после паузы в 3 сек. В примере поток будет повторятся постоянно, без остановки:
import {defer} from 'rxjs'
import {delayWhen, tap, retryWhen, mapMerge} from 'rxjs/operators'
import axios from 'axios'
const params = {}
const request$ = defer(_ => {
return axios.get('/some/url', params)
}).pipe(
// при возникновении ошибки
// повторяем последовательность снова
retryWhen(errors => {
return errors.pipe(
tap(_ => console.log('request error...')),
// выдерживаем паузу в 3 секунды
delayWhen(_ => rx.timer(3000))
)
}))
Для более правильной работы нам необходимо увеличивать интервал повторения запроса и лимит повторений, а также игнорируемые HTTP-статусы:
import {} from 'rxjs'
import {} from 'rxjs/operators'
import axios from 'axios'
const retryStrategy = function(opts){
opts = opts || {}
let maxRetry = opts.maxRetry || 3
let scaling = opts.scaling || 3000
let excluded = opts.excluded || []
return function(attempts){
return attempts.pipe(
mergeMap((error, i) => {
const retryAttempt = i + 1
if (
retryAttempt > maxRetry ||
excluded.find(e => e === error.status )
) {
return _throw(error)
}
console.log(`attempt: ${retryAttempt}, retrying in: ${retryAttempt * scaling}ms`)
return timer(retryAttempt * scaling)
}),
finalize(_ => console.log('we are done'))
)
}
}