rxjs icon indicating copy to clipboard operation
rxjs copied to clipboard

Unexpected behavior for retry with delay operator in angular on interceptor

Open adrian-badulescu opened this issue 2 years ago • 2 comments

Describe the bug

I got a very weird behavior from my retry on error Angular interceptor. If I am using the simple form of retry operator -- retry(2) the interceptor does what is supposed to do, retry twice, but if I am using the retry object configurator -- RetryConfig retry({ count: 2, delay: 2000, resetOnSuccess: true }) then I got the effect of Infinity mentioned -- here in the documentation retry<T>(configOrCount: number | RetryConfig = Infinity): MonoTypeOperatorFunction<T>

It is worth to be mentioned that if I am using retry with the configuration object outside the Interceptor -- directly over HttpClient class, it works perfectly as for example:

import { Inject, Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { catchError, retry } from 'rxjs/operators';


@Injectable({
  providedIn: 'root'
})
export class WhoisService {
  private _baseUrl: string = '';

  constructor(private http: HttpClient, @Inject('BASE_URL') baseUrl: string) {
    this._baseUrl = baseUrl;
  }

  searchUrl(domainQueried: string): Observable<boolean> {
    return this.http.get<boolean>(`${this._baseUrl}getdomainN/Query?id=${domainQueried}`)
      .pipe(
        retry({ count: 2, delay: 2000, resetOnSuccess: true }),
        catchError(error => of(error))
      )
  }


}

The Interceptor:

import { Injectable } from '@angular/core';
import {
  HttpRequest,
  HttpHandler,
  HttpEvent,
  HttpInterceptor,
  HttpErrorResponse
} from '@angular/common/http';
import { catchError, Observable, of, retry, throwError } from 'rxjs';

@Injectable()


export class ErrorInterceptor implements HttpInterceptor {

  constructor() { }

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next.handle(request)
    .pipe(
      //retry(2) <-- this works but without delay
      retry({ count: 2, delay: 2000, resetOnSuccess: true }), // <-- Infinity of retries
      catchError(error => of(error)),

    )



  }


}

Maybe someone can explain to me what is happening.

Thank you

Expected behavior

To do retry on http errors, twice with 2 seconds delay

Reproduction code

No response

Reproduction URL

No response

Version

~7.5.5

Environment

{ "name": "newtd", "version": "0.0.0", "scripts": { "ng": "ng", "prestart": "node aspnetcore-https", "start": "run-script-os", "start:windows": "ng serve --port 44417 --ssl --ssl-cert %APPDATA%\ASP.NET\https\%npm_package_name%.pem --ssl-key %APPDATA%\ASP.NET\https\%npm_package_name%.key", "start:default": "ng serve --port 44417 --ssl --ssl-cert $HOME/.aspnet/https/${npm_package_name}.pem --ssl-key $HOME/.aspnet/https/${npm_package_name}.key", "build": "ng build", "build:ssr": "ng run newTD:server:dev", "watch": "ng build --watch --configuration development", "test": "ng test" }, "private": true, "dependencies": { "@angular/animations": "^14.0.3", "@angular/common": "^14.0.3", "@angular/compiler": "^14.0.3", "@angular/core": "^14.0.3", "@angular/forms": "^14.0.3", "@angular/platform-browser": "^14.0.3", "@angular/platform-browser-dynamic": "^14.0.3", "@angular/platform-server": "^14.0.3", "@angular/router": "^14.0.3", "@faker-js/faker": "^7.6.0", "bootstrap": "^5.1.3", "jquery": "^3.6.0", "oidc-client": "^1.11.5", "popper.js": "^1.16.0", "run-script-os": "^1.1.6", "rxjs": "~7.5.5", "tslib": "^2.4.0", "zone.js": "~0.11.6" }, "devDependencies": { "@angular-devkit/build-angular": "^14.0.3", "@angular/cli": "^14.0.3", "@angular/compiler-cli": "^14.0.3", "@types/jasmine": "~4.0.3", "@types/jasminewd2": "~2.0.10", "@types/node": "^18.0.0", "jasmine-core": "~4.2.0", "karma": "~6.4.0", "karma-chrome-launcher": "~3.1.1", "karma-coverage": "~2.2.0", "karma-jasmine": "~5.1.0", "karma-jasmine-html-reporter": "^2.0.0", "typescript": "~4.7.4" }, "overrides": { "autoprefixer": "10.4.5" } }

Additional context

Angular app with .Net Core 6 scaffolded in Visual Studio

adrian-badulescu avatar Feb 21 '23 10:02 adrian-badulescu

I encountered same issue with RXJS 7.8.0 and angular 15.2.8. retryCount is always 1 and infinite retry without delay.

Every retried request is reintercepted by interceptor.

I haven't found a workaround yet.

Lougnar avatar Jun 13 '23 15:06 Lougnar

Maybe someone can explain to me what is happening.

Hi @adrian-badulescu and @root14 !

I'm pretty sure that this is what's happening:

next.handle(request) returns an Observable that when it errors, if you synchronously re-subscribe to it , then it synchronously throws the error again.

However, if you re-subscribe to that same Observable after a while (instead of re-subscribing to it synchronously), then the Observable starts emitting events again... until it eventually errors.

What happens is that since you have the resetOnSuccess set to true, then the count starts over every time that the Observable emits a next value (rather than emitting an error). So, there you go: that's what's happening.

It is worth to be mentioned that if I am using retry with the configuration object outside the Interceptor -- directly over HttpClient class, it works perfectly as for example:

I know nothing about Angular, but I'm willing to bet that the Obseravble returned from this.http.get<boolean> doesn't emit intermediate values, thus never having an emission that resets the counter. That's why it works on this.http.get but it doesn't work on the interceptor (which must be emitting intermediate values before error/complete).

josepot avatar Jun 14 '23 06:06 josepot