Home > Enterprise >  How to emit values into timer creation function RXJS & Angular
How to emit values into timer creation function RXJS & Angular

Time:11-17

I am trying to create a retry config for retry after catchError. I need to send specific delays based on values in an array. I want to be able to take each value from the array and send them individually to a timer creation function.

so for example:

numberArr = [1000, 2000, 3000]

timer is called with timer(1000), then timer(2000), then timer(3000)

using concatMap only uses the first value, switchMap the last, but I am unsure of what to use to take each emitted number and use that value for the timer function.

Any suggestions would be welcome.

   private ob$ = of(1);

   private retryConfig: {
     matcher: () => void;
     retryWaits: number[];
   };

   constructor() {}

   private setRetryConfig(): RetryConfig {
    this.retryConfig = {
      matcher: () => {},
      retryWaits: [3000, 4000, 6000],
    };

    const delayObservable$ = from(this.retryConfig.retryWaits)

    const delayFunction = delayObservable$.pipe(
      tap(() => console.time()),
      tap((x) => console.log('delay', x)),
      concatMap((number) => timer(number)), // this is where I am seeing a problem
      take(this.retryConfig.retryWaits.length),
      tap(() => console.timeEnd()) // this is alway logging 3000ish milliseconds
    );

    return {
      count: this.retryConfig.retryWaits.length,
      delay: () => delayFunction,
    };
  }

   ngOnInit(): void {
    this.ob$
      .pipe(
        tap(() => {
          throw throwError(() => new Error('error'));
        }),
        catchError((error) => throwError(() => new Error(error))),
        retry(this.setRetryConfig())
      )
      .subscribe((x) => {
        if (x) {
          throw throwError(() => new Error('error'));
        }
      });
   }

CodePudding user response:

The delay function gets called everytime the error is thrown. We can't use the array of numbers to wait x times. Instead, we need to use whatever the current retry-wait value is and wait once.

We can achieve this by calling timer in the delay callback and passing it the first element of retryWaits simultaneously removing it from the array.

const setRetryConfig = () => {
  const retryConfig = {
      matcher: () => {},
      retryWaits: [3000, 4000, 6000],
  };

  return {
    count: retryConfig.retryWaits.length,
    delay: () => timer(retryConfig.retryWaits.shift())
  } 
} 

Also note that throwing an Error inside a tap might not work. I threw the error in a mergeMap instead.

of(0).pipe(
  mergeMap(() => {
    console.log("trying")
    return throwError(() => 'Error!');
  }),
  retry(setRetryConfig())
).subscribe(r => console.log("Result", r))

This outputs:

trying
---0s---
---1s---
---2s---
trying
---3s---
---4s---
---5s---
---6s---
---7s---
trying
---8s---
---9s---
---10s---
---11s---
---12s---
---13s---
---14s---
trying
---15s---
Error!
  • Related