Rxjs重试延迟功能

时间:2017-07-07 20:28:36

标签: angular typescript rxjs

我正在尝试将retrydelay函数一起使用,我希望函数会在1000ms延迟后调用,但它不会,这里有什么错误? 看看控制台输出,它同时是16:22:48。

我希望有16:22:48,16:22:59 ......

canCreate: boolean;
getSomeFunction(): Observable<boolean> {
        return new Observable<boolean>(
            observer => {
                const canCreate = null; // this is just null for now, will some value later
                if (canCreate == null) {
                    observer.error('error');
                } else {
                    observer.next(true);
                }
                observer.complete();
            }
        )
    }


this.getSomeFunction()
      .do((value) => {
        this.cCreate = value;
      }, (error) => {
         console.log(error + new Date().toTimeString());
      })
      .delay(1000)
      .retry(10)
      .subscribe(
        value => this.cCreate = value,
        error => {
          this.cCreate = false;
        },
        () => {}
      );
  }

和控制台结果是:

enter image description here

10 个答案:

答案 0 :(得分:42)

delay()用于在observable发出的事件之间引入延迟。但是观察者永远不会发出任何事件。它只会立即出错。

您正在寻找的是retryWhen(),它允许在重试多长时间后决定:

RxJS 5:

  .retryWhen(errors => errors.delay(1000).take(10))

RxJS 6:

import { retryWhen, delay, take } from 'rxjs/operators'
someFunction().pipe(
  // ...
  retryWhen(errors => errors.pipe(delay(1000), take(10)))
)

这将在10次尝试后完成整个观察。如果你想在10次尝试后出错整个observable,那么retryWhen回调返回的observable必须抛出:

RxJS 5:

  .retryWhen(errors => errors.delay(1000).take(10).concat(Observable.throw()))

RxJS 6:

import { retryWhen, delay, take, concatMap, throwError } from 'rxjs/operators'
someFunction().pipe(
  // ...
  retryWhen(errors => errors.pipe(delay(1000), take(10), concatMap(throwError)))
)

答案 1 :(得分:21)

添加@JB Nizet的回答。如果您使用lettable运算符在rxjs 5+中编写此代码,请将其构造为

@Keep private void CallbackHandler(String string_data, int int_data) { // Some Code }

答案 2 :(得分:2)

所有这些都是RxJS 6 +


TL; DR

您可以使用此软件包中经过全面测试的运算符,或向下滚动以查看源代码:)

npm i rxjs-boost
import { retryWithDelay } from 'rxjs-boost/operators';

obs$.pipe(
  // will retry 4 times with a 1s delay before each try:
  retryWithDelay(1000, 4)
);

条件

由于其他大多数答案(或可能没有答案)都不符合我的所有标准,因此我将在下面列出解决方案。目标:

  • 如果没有错误抛出,则定期发射并完成。 ✅
  • 如果引发错误,则重试x次。 ✅
  • 在每次重试之前延迟y。 ✅
  • 返回上一个发出的错误。(很多其他答案都在为此苦苦挣扎。)✅
  • 使用strict: true正确键入-但这很难弄乱。 ✅

解决方案

与其他所有答案一样,我们将使用retryWhen运算符来捕获错误。要跟踪重复次数,可以使用scan运算符。为了限制重复次数,我们只需要在map运算符内抛出一个错误即可。

原始来源使用throwIf,但是在那种情况下,我们可以简单地使用retryWithDelay中的rxjs-boost

最后,我们将使用delay运算符来添加不同执行之间的延迟:

import { MonoTypeOperatorFunction } from 'rxjs';
import { delay as delayOperator, map, retryWhen, scan } from 'rxjs/operators';

export function retryWithDelay<T>(
  delay: number,
  count = 1
): MonoTypeOperatorFunction<T> {
  return (input) =>
    input.pipe(
      retryWhen((errors) =>
        errors.pipe(
          scan((acc, error) => ({ count: acc.count + 1, error }), {
            count: 0,
            error: undefined as any,
          }),
          map((current) => {
            if (current.count > count) {
              throw current.error;
            }
            return current;
          }),
          delayOperator(delay)
        )
      )
    );
}

来源

答案 3 :(得分:1)

这可能对您有所帮助

let values$ = Rx.Observable.interval(1000).take(5);
let errorFixed = false;

values$
.map((val) => {
   if(errorFixed) { return val; }
   else if( val > 0 && val % 2 === 0) {
      errorFixed = true;
      throw { error : 'error' };

   } else {
      return val;
   }
})
.retryWhen((err) => {
    console.log('retrying again');
    return err.delay(1000).take(3); // 3 times
})
.subscribe((val) => { console.log('value',val) });

答案 4 :(得分:1)

  

在rxjs版本6.3.3上工作

     

https://stackblitz.com/edit/http-basics-8swzpy

     

打开控制台并查看重试

     

示例代码

import { map, catchError, retryWhen, take, delay, concat } from 'rxjs/operators';
import { throwError } from 'rxjs';


export class ApiEXT {

    static get apiURL(): string { return 'http://localhost:57886/api'; };
    static httpCLIENT: HttpClient;

 static POST(postOBJ: any, retryCOUNT: number = 0, retryITNERVAL: number = 1000) {
        return this.httpCLIENT
            .post(this.apiURL, JSON.stringify(postOBJ))
            .pipe(
                map(this.handleSUCCESS),
                retryWhen(errors => errors.pipe(delay(retryITNERVAL), take(retryCOUNT), concat(throwError("Giving up Retry.!")))),
                catchError(this.handleERROR));
    }


  private static handleSUCCESS(json_response: string): any {
        //TODO: cast_and_return    
        return JSON.parse(json_response);

    }

 private static handleERROR(error: Response) {
        let errorMSG: string;
        switch (error.status) {
            case -1: errorMSG = "(" + error.status + "/" + error.statusText + ")" + " Server Not Reachable.!"; break;
            default: errorMSG = "(" + error.status + "/" + error.statusText + ")" + " Unknown Error while connecting with server.!"; break;
        }
        console.error(errorMSG);
        return throwError(errorMSG);
    }

}

答案 5 :(得分:1)

我得出这个结论,是为了重试http管道中的其他操作

import {delay as _delay, map, retryWhen} from 'rxjs/operators';

export const delayedRetry = (delay, retries = 1) => retryWhen(result => {
    let _retries = 0;
    return result.pipe(
      _delay(delay),
      map(error => {
        if (_retries++ === retries) {
          throw error;
        }
        return error;
      }),
    );
  },
);

用法

    http.pipe(
      delayedRetry(1500, 2),
      catchError((err) => {
        this.toasterService.error($localize`:@@not-saved:Could not save`);
        return of(false);
      }),
      finalize(() => this.sending = false),
    ).subscribe((success: boolean) => {
        if (success === true) {
           this.toasterService.success($localize`:@@saved:Saved`);
        }
      }
    });

答案 6 :(得分:0)

我使用retryWhenObservable.Interval提出了以下解决方案,但在此解决方案中,订阅的error函数从不调用,

this.branchService.getCanCreate()
  .do((value) => {
    this.cCreate = value;
  }, (error) => {
    console.log('do', error + new Date().toTimeString());
  })
  .retryWhen(errors => {
    return Observable.interval(1000).take(3).concat(Observable.throw('error')));
  })
  .subscribe(
    value => {
      this.cCreate = !!value
      console.log('success', new Date().toTimeString());
    },
    error => {
      console.log('subscribe', error + new Date().toTimeString());
      this.cCreate = false;
    },
    () => {
      console.log('finally', new Date().toTimeString());
    }
  );

答案 7 :(得分:0)

对于ngrx5 +,我们可以创建运算符:


function retryRequest(constructor: () => Observable, count: number, delayTime: number) {
  let index = 0;
  return of(1) // we need to repeat not the result of constructor(), but the call of constructor() itself
    .pipe(
      switchMap(constructor),
      retryWhen(errors => errors.pipe(
        delay(delayTime),
        mergeMap(error => {
          if (++index > count) {
            return throwError(error);
          }
          return of(error);
        })
      ))
    );
}

答案 8 :(得分:0)

RxJS提供了重试运算符,该运算符在出现错误时为给定的计数重新订阅Observable。在引发错误之前,重试运算符将Observable重新分配给定的计数数量,如果仍然存在错误,则引发错误。重试有助于多次访问URL。由于网络带宽的原因,URL可能不会一次返回成功的数据,而在重新绑定时,URL可能会成功返回数据。如果重新输入后Observable中仍然存在错误,则catchError可用于返回带有用户定义的默认数据的Observable。

getBook(id:number):可观察{   返回this.http.get(this.bookUrl +“ /” + id).pipe(      重试(3),      catchError(err => {       console.log(err);       返回(null);      })   ); }

答案 9 :(得分:0)

我最近遇到了这个问题,发现可以改善公认的解决方案。

Observable.pipe(
     retryWhen(errors => errors.pipe(
      delay(1000),
      take(10))),
    first(v => true),
    timeout(10000))

本质上,它所做的是重试,但是无需使用“ first”运算符添加任何(错误的)值即可立即完成。

如果在超时时间内找不到值,则会引发错误。