【问题标题】:Observables "retryWhen" delay可观察的“retryWhen”延迟
【发布时间】:2017-01-01 23:38:14
【问题描述】:

如何在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

【问题讨论】:

标签: javascript angular observable


【解决方案1】:
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

【讨论】:

    【解决方案2】:

    有点晚了,但这是新版本的 rxjs (>6) 的处理方法 我猜您正在尝试在发生故障时自动重试网络。 这可以通过不同的方式来实现,但这里是一个非常小的实现 RetryWhen() - 如果任何错误被 observable 抛出,THis 操作符会捕获并从中创建 errorObservable。 现在使用 errorObservable,我们可以针对特定失败集重试指定次数的尝试

    例如,在 404 失败的情况下重试,确实没有必要,但在 500X 异常情况下,它确实是强制性的。

    DelayWhen - 我们可以使用这个操作符来指定一个 timer observable,它会延迟下一次重试尝试,直到时间过去。这也提供了线性增加每次重试尝试之间的延迟的额外优势

    iif - 使用此条件运算符确实使我们能够根据给定条件过滤和执行必要的 observable。您也可以在 stackoverflow 中举例。但我会给出一个简单的 if else 说明

    contactMap - 这是高阶 observable 运算符,这意味着它产生 /maps 一个输入/observable 到一个输出 observable。使用它的原因是,我们需要重试重试操作以防相同的失败指定次数,并且重新启动操作的最佳方法是通过错误 Observable。 请注意,我们可以使用其他高阶可观察操作符,例如 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

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-01-04
        • 1970-01-01
        • 2017-07-13
        • 1970-01-01
        • 1970-01-01
        • 2021-06-26
        • 2020-07-10
        • 1970-01-01
        相关资源
        最近更新 更多