Observables“retryWhen”延迟

时间:2016-08-25 13:40:43

标签: javascript angular observable

如何在retryWhen中设置延迟?

import 'rxjs/add/operator/retry';
import 'rxjs/add/operator/retrywhen';

...

constructor(http: Http) {

  var headers = new Headers();
  headers.append('Content-Type', 'text/plain');
  http.post('https://mywebsite.azurewebsites.net/account/getip', "", { headers: headers })
     .retryWhen(errors => {
                return errors.delay(1000);   // errors is not a function
              })
             (event) => {
                // handle events
                this.ip = event.json();
            },
            (error) => {
                console.error(error);
                toastr.error('No connection to server. Please reload the page!')

            }
            );
 }

我收到错误:errors is not a function

3 个答案:

答案 0 :(得分:6)

import {Http, Headers, Response} from '@angular/http';

http.get('http://jsonplaceholder.typicode.com/posts/1/commentsss')
    .retryWhen(e => e.scan<number>((errorCount, err) => {
        if (errorCount >= 10) {
            throw err;
        }
        return errorCount + 1;
    }, 0).delay(1000))
    .subscribe(r => console.log(`Sub: ${r}`))

重试10次,延迟1秒。

plnkr

答案 1 :(得分:2)

有点晚了,但这是使用新版本的rxjs(> 6)的方法 我猜您正在尝试自动重试网络,以防出现故障。 这可以通过不同的方式来实现,但这是一个很小的实现 RetryWhen()-此运算符将捕获可观察对象是否抛出任何错误,并从中创建errorObservable。 现在,使用errorObservable可以针对特定的一组失败重试指定次数的尝试

例如,在404错误的情况下重试确实是没有必要的,但是在500X异常的情况下,这实际上是强制性的。

DelayWhen (延迟时间)-我们可以使用此运算符指定可观察的计时器,从而将下一次重试尝试延迟到该时间过去为止。这还提供了额外的优势,即可以线性地增加每次重试之间的延迟时间

iif -使用此条件运算符确实使我们能够根据给定条件过滤并执行必要的可观察值。您也可以在stackoverflow中举例。但是我要给出一个简单的例子

contactMap -这是可观察的高阶运算符,这意味着它将输入/可观察的输入生成/映射到可观察的输出。使用此命令的原因是,如果相同的故障发生了指定的次数,我们需要重做重试操作,而重启操作的最佳方法是通过可观察到的错误。 请注意,我们可以使用其他更高阶的可观察运算符,例如mergeMap / switchMap-每个都有其优缺点,但是我更喜欢使用它。再次,contactMap与concat运算符不同,因此请注意此处

我发现在Angular中实现此类重试的最佳位置是在httpinterceptors内部,但这也可以在服务内部完成

这是一个示例实现:

// Class to intercept all network calls and retry for X number of times
export class ErrorInterceptor implements HttpInterceptor {
  //   private authService: UserAuthenticationService;
  private ngxLogger: NGXLogger;
  private errorHandlerService: ErrorHandlerService;
  constructor(injector: Injector) {
    this.ngxLogger = injector.get(NGXLogger);
    this.errorHandlerService = injector.get(ErrorHandlerService);
  }

  intercept(
    req: HttpRequest<any>,
    next: HttpHandler
  ): Observable<HttpEvent<any>> {


    const interceptObs$ = next.handle(req);
// you could filter for the URLS that this should be applied like this
    if (req.url.match(API_FETCH_TOKEN)) {
      let retryAttempts = 0;
      const httpInterceptor$ = interceptObs$.pipe(
        retryWhen(errorObs => {

          return errorObs.pipe(
            tap(errval => console.log(`Retrying because of err:${errval}`)),

            concatMap(errObsValue => {
              if (
                errObsValue instanceof HttpErrorResponse &&
                errObsValue.status != 404
              ) {
                console.log('inside concatMap', errObsValue);

                if (retryAttempts > APP_NET_RETRIES) {
                  return throwError(this.getErrorModel(errObsValue, req));
                } else {
                  retryAttempts++;
// this linearly increases the delay of attempts
                  delayWhen(() =>
                    timer(retryAttempts * APP_NET_RETRIES_DELAY * 1000)
                  );
                  return httpInterceptor$;
                }
              }
            })
          );
        })
      );
      return httpInterceptor$;
    } else {
      return interceptObs$;
    }
    // above is the notifier observable, with errors captured as input observable
    // so we could operate on this observable for retires
    // also make sure to return the error observable - i.e the notifier observable
    // reason being all errors should again be returned so as to capture them and
 // only when they are returned they can be retried
    // Also make sure after take() - no.of retries we just throw a last occuring error obs




  }

// I like to create a custom error model and handle that in Custom ErrorHandler
// Below is the sample implementation I use. but again this is your preference
// you can just rethrow the error alone
  async getErrorModel(errValue, req): Promise<ErrorModel> {
    const errModel = new ErrorModel();
    errModel.Error = errValue;
    errModel.ErrorMessage = 'Error in retrieving the token:' + errValue.message;
    errModel.ErrorStatus = 421;
    errModel.ErrorUrl = req.url;
    errModel.Timestamp = momentTimeZone
      .tz(DEFAULT_TIME_ZONE)
      .format(DEFAULT_TIME_ZONE);
    await this.errorHandlerService.handleError(errModel);
    return Promise.resolve(errModel);
  }
}

希望它会有所帮助。

编辑:关于backoff-rxjs确实有一篇不错的文章,它确实缩短了我们上面所做的工作。请参阅this link

答案 2 :(得分:0)

我找到了这个页面,并做了一些改进:

retryWhen(errors => errors.pipe(
  takeWhile((err, index) => {
    if (index === 3) throw new Error(err.toString());
    return true
  }),
  delay(1000),
))