Как условно повторить попытку при нажатии на действие из закусочной Angular 7.x - PullRequest
0 голосов
/ 12 февраля 2019

Я разрабатываю функцию повтора автоматически, ErrorInterceptor реализуя HttpInterceptor.Я прочитал, что retryWhen это именно то, что мне нужно.То, что я хочу, чтобы это произошло, - это условная повторная попытка по нажатию на кнопку Snackbar 'RETRY', и я не хочу, чтобы она запускала неограниченные запросы (поэтому, возможно, после 3 раз вы не сможете повторить попытку).Проблема в том, что теперь я не знаю, как условно повторить попытку после нажатия кнопки действия на панели уведомлений

Я уже пытался просто запустить службу уведомлений, которая отображает уведомление с простым кодом:


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)).

enter image description here

Любая помощь очень ценится.

1 Ответ

0 голосов
/ 19 августа 2019

Он пропускает его и возвращает ошибку, потому что, насколько я понимаю, это то, что вы делаете внутри своей mergeMap, вы отправляете уведомление (с обратным вызовом), а затем return throwError(error);.

непроверенный код, которому нужно следовать.

Измените службу уведомлений так, чтобы она возвращала наблюдаемое:

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, но лично это кажется бессмысленным, как действие закусочной)следует просто запустить повтор 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!')),
    );
  };
...