1

我正在通过ErrorInterceptor实现自动开发重试功能HttpInterceptor。我读到这retryWhen正是我所需要的。我希望它发生的是有条件地重试点击 Snackbar 的“重试”按钮,我不希望它触发无限的请求(所以,也许 3 次后你不能再试一次)。问题是现在我不知道如何在单击通知小吃栏上的操作按钮后有条件地重试

我已经尝试触发 notificationService ,它使用简单的代码显示通知:


intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next.handle(request).pipe(
      retryWhen(error => {
        this.notificationService.showNotification('problem RETRY', NotificationActionLabel.Retry, 4000, () => {
          console.log('retrying');
          return of(error);
        });
        return throwError(error);
      }),

这不再重试该功能,它只是停止。

然后我找到了一些关于 retryWhen 的代码,它描述了一个 generalRetryStrategy https://www.learnrxjs.io/operators/error_handling/retrywhen.html。我添加了这个,但我希望它有条件地触发(链接到操作按钮)。

我在通知服务上有一个回调函数

export class NotificationService {
  constructor(private snackBar: MatSnackBar) {}

  public showNotification(message: string, action?: string, duration?: number, callBackFunction?: Function) {
    const defaultDuration: number = 2500;
    const notificationRef = this.snackBar.open(message, action, {
      duration: duration == null ? defaultDuration : duration,
      panelClass: ['snackbar-styling'],
    });

    notificationRef.onAction().subscribe(result => callBackFunction(result));
  }
}

拦截器现在如下:

intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next.handle(request).pipe(
      retryWhen(this.genericRetryStrategy()),
      catchError((error: HttpErrorResponse) => {
        let errorMessage = '';

        // Client side error
        if (error.error instanceof ErrorEvent) {
          errorMessage = `Error: ${error.error.message}`;
        } else {
          // Server Side Error
          errorMessage = this.generateErrorMessage(error);
        }

        this.loggingService.logErrorMessage(error.error.message);
        this.notificationService.showNotification(errorMessage, null, 4000);
        return throwError(error.error.message);
      }),
    );

使用以下函数 genericRetryStrategy,请注意这与 learn-rxjs 中的代码几乎没有什么不同。

 genericRetryStrategy = ({
    maxRetryAttempts = 3,
    scalingDuration = 1000,
    excludedStatusCodes = [],
  }: {
    maxRetryAttempts?: number;
    scalingDuration?: number;
    excludedStatusCodes?: HttpStatusCode[];
  } = {}) => (attempts: Observable<any>) => {
    return attempts.pipe(
      mergeMap((error, i) => {
        this.notificationService.showNotification('attempting', 'retry', 4000, () => {
          const retryAttempt = i++;
          // if maximum number of retries have been met
          // or response is a status code we don't wish the retry, throw error
          if (retryAttempt > maxRetryAttempts || excludedStatusCodes.find(e => e === error.status)) {
            return throwError(error);
          }

          console.log(`Attempt ${retryAttempt}: retrying in ${retryAttempt * scalingDuration}ms`);

          //retry after 1s, 2s, etc...
          return timer(retryAttempt * scalingDuration);
        });
        return throwError(error);
      }),
      finalize(() => console.log('We are done!')),
    );
  };

我希望它仅在真正调用通知服务回调函数时才触发重试功能(因此单击“重试”按钮)。现在它立即跳过通知服务调用并返回错误(throwError(error))

在此处输入图像描述

任何帮助深表感谢。

4

1 回答 1

0

它跳过它并返回错误,因为据我所知,这是您在 mergeMap 中所做的,您发送通知(带有回调),然后return throwError(error);.

遵循未经测试的代码。

更改通知服务以返回 observable:

export class NotificationService {
  constructor(private snackBar: MatSnackBar) {}

  public showNotification(message: string, action?: string, duration?: number, callBackFunction?: Function) {
    const defaultDuration: number = 2500;
    const notificationRef = this.snackBar.open(message, action, {
      duration: duration == null ? defaultDuration : duration,
      panelClass: ['snackbar-styling'],
    });

    notificationRef.onAction().subscribe(result => callBackFunction(result));
  }
}

然后尝试这样的事情(也许放下计时器,目前它正在等待显示snackBar,但就个人而言,这似乎毫无意义,因为snackbar Action 应该只是触发重试IMO):

let genericRetryStrategy = ({
    maxRetryAttempts = 3,
    scalingDuration = 1000,
    excludedStatusCodes = [],
  }: {
    maxRetryAttempts?: number;
    scalingDuration?: number;
    excludedStatusCodes?: HttpStatusCode[];
  } = {}) => (attempts: Observable<any>) => {
    return attempts.pipe(
      mergeMap((error, i) => {
        const retryAttempt = i + 1;
        if (retryAttempt > maxRetryAttempts || excludedStatusCodes.find(e => e === error.status)) {
          return throwError(error);
        }
        console.log(`Attempt ${retryAttempt}: retrying in ${retryAttempt * scalingDuration}ms`);
        return timer(retryAttempt * scalingDuration).pipe(
          switchMap(this.notificationService.showNotification('attempting', 'retry', 4000))
        );
      }),
      finalize(() => console.log('We are done!')),
    );
  };
于 2019-08-19T09:40:27.790 回答