Как создать RXjs RetryWhen с задержкой и ограничением попыток

Я пытаюсь сделать вызов API (используя angular4), который повторяет, когда он терпит неудачу, используя retryWhen. Я хочу, чтобы он задерживался на 500 мс и снова повторил попытку. Это может быть достигнуто с помощью этого кода:

loadSomething(): Observable<SomeInterface> {
  return this.http.get(this.someEndpoint, commonHttpHeaders())
    .retryWhen(errors => errors.delay(500));
}

Но это будет продолжаться постоянно. Как ограничить это, допустим, 10 раз?

Спасибо!

Ответ 1

Вам необходимо применить ограничение к сигналу повторной попытки, например, если вы хотите только 10 повторных попыток:

loadSomething(): Observable<SomeInterface> {
  return this.http.get(this.someEndpoint, commonHttpHeaders())
    .retryWhen(errors => 
      // Time shift the retry
      errors.delay(500)
            // Only take 10 items
            .take(10)
            // Throw an exception to signal that the error needs to be propagated
            .concat(Rx.Observable.throw(new Error('Retry limit exceeded!'))
    );
}

EDIT

Некоторые комментаторы спрашивали, как сделать так, чтобы последняя ошибка была выброшена. Ответ немного менее ясен, но не менее эффективен, просто используйте один из операторов сглаживающей карты (concatMap, mergeMap, switchMap), чтобы проверить, к какому индексу вы принадлежите.

Примечание. Использование нового синтаксиса RxJS 6 pipe для дальнейшей проверки (это также доступно в более поздних версиях RxJS 5).

loadSomething(): Observable<SomeInterface> {
  const retryPipeline = 
    // Still using retryWhen to handle errors
    retryWhen(errors => errors.pipe(
      // Use concat map to keep the errors in order and make sure they
      // aren't executed in parallel
      concatMap((e, i) => 
        // Executes a conditional Observable depending on the result
        // of the first argument
        iif(
          () => i > 10,
          // If the condition is true we throw the error (the last error)
          throwError(e),
          // Otherwise we pipe this back into our stream and delay the retry
          of(e).pipe(delay(500)) 
        )
      ) 

  return this.http.get(this.someEndpoint, commonHttpHeaders())
    // With the new syntax you can now share this pipeline between uses
    .pipe(retryPipeline)
}