What is the best way to retry an action stream observable in Angular/RxJS after it fails?

Kindly disregard the variable names and formatting alterations I've made.

I've been attempting to incorporate RxJS error handling for an observable that triggers an action (user click) and then sends the request object from our form to execute an HTTP call, as demonstrated below.

Problem: When I send a request (from a form) on our website that doesn't exist in the backend, it fails and returns an error (which is good!), BUT if I try to submit a correct request after the failed attempt, the application just hangs and loads without any response.

serivce.ts file

sendRequest$ = this.exampleRatingSubjectAction$.pipe(switchMap(exampleRequest => this.http.post<exampleresponse>(this.PCFUrl + '/example',exampleRequest).pipe(
  catchError(this.handleError)
 )),
 tap(data => console.log(data)),
 );

sendExampleRequestEconomy$ = this.exampleSubjectActionEconomy$.pipe(switchMap(exampleRequest=>  this.http.post<exampleresponse>(this.PCFUrl + '/example',exampleRequest)
 .pipe(
  catchError(this.handleError)
 )),
 tap(data => console.log(data)),
 );

private handleError(err: HttpErrorResponse): Observable<never> {
// in a real world app, we may send the server to some remote logging infrastructure
//instead of just logging it to the console
let errorMessage: string;
if (err.error instanceof ErrorEvent) {
// A client-side or network error occured. Handle it accordingly
errorMessage = `An error occured: ${err.error.message}`;
} else {
// The backend returned an unsuccessful response code.
// the resposne body may contain clues as to what went wrong,
errorMessage = `Backend returned code ${err.status}: ${err.message}`;
}
console.error(err);
return throwError(errorMessage);
}

component code:

exampleResponsePriority$ = this.dataService.sendExampleRequest$
.pipe(
   map(data => this.exampleResponsePriority = data),
   tap(data => { 
    this.showPageLoader = false; 
    this.calculatorService.checkMinOrDeficitRate(data);
}),
catchError( err => {
   this.showPageLoader = false;
   // await this.presentAlert(err);
   this.errorMessageSubject.next(err);
   console.log("priority")
   return EMPTY;
}));

exampleResponseEconomy$ = this.dataService.sendExampleRequestEconomy$
.pipe(
  map(data => this.exampleResponseEconomy = data),
  catchError( err => {
    this.showPageLoader = false;
    // await this.presentAlert(err);
    this.errorMessageSubject.next(err);
    console.log("economy")
   return EMPTY;
}));

My only speculation is that our action stream has already been updated with a request object on the first click, so when the backend response is a 500 error, it might be stuck and not allowing for a resubmit?

I've attempted to modify the action stream upon a failed request, but couldn't achieve the desired result.

I believe I'm just a few lines of code away!

Any assistance would be greatly appreciated.

Answer №1

Issue

It seems that in the final catchError block within your component code, you are returning the EMPTY RxJS stream as the error response. The RxJS documentation states that:

This operator deals with errors and passes all other events to the resulting observable. If the source observable ends with an error, it will map that error to a new observable, subscribe to it, and pass on all its events to the resulting observable.

You can find more information on this in the official documentation, with emphasis provided.

Since your source observer ended with an error, it skips those previous steps in the pipeline. Consequently, the subscription moving forward is listening to EMPTY (documentation link), causing the subscription handler not to be invoked (and the subscription itself not to end).

Solution

A potential solution could be to return the source observable passed to catchError as the second argument within the handler. This adjustment may look like the following in your code:

exampleResponsePriority$ = this.dataService.sendExampleRequest$
.pipe(
   map(data => this.exampleResponsePriority = data),
   tap(data => { 
    this.showPageLoader = false; 
    this.calculatorService.checkMinOrDeficitRate(data);
}),
catchError((err, caught) => {
   this.showPageLoader = false;
   // await this.presentAlert(err);
   this.errorMessageSubject.next(err);
   console.log("priority")
   return caught;
}));

You might also want to explore the retry operator documentation, which can serve as an alternative or complement to catchError for achieving similar outcomes.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

When exporting data to Excel, the date is automatically adjusting based on the timezone. Is there a way to prevent this from happening

Whenever I attempt to export data to excel in angular, I encounter an issue with time zones. The date in the database is set in timezone 'x', while I am exporting data from timezone 'y'. As a result, the exported data in Excel displays ...

Did the IBM MobileFirst client miss the call to handleFailure?

I am currently utilizing the IBM MFP Web SDK along with the provided code snippet to send challenges and manage responses from the IBM MobileFirst server. Everything functions properly when the server is up and running. However, I have encountered an iss ...

Using TypeScript to define task invocation parameters with AWS CDK's CfnMaintenanceWindowTask

Currently, I am utilizing AWS CDK along with the library @aws-cdk/aws-ssm and TypeScript to construct CfnMaintenanceWindowTask. The code example I am working on is derived from AWS CloudFormation documentation, specifically for "Create a Run Command t ...

activating serverless.yml for aws-xray

I have been attempting to implement AWS X-Ray for all lambda functions in the following manner: serverless.yml provider: tracing: lambda: true apiGateway: true name: aws runtime: nodejs8.10 stage: ${opt:stage, 'dev'} region: ...

How to modify the background color within the mat-menu-panel

Incorporating angular 9 and less into my current project, I have encountered an issue with a mat-menu-panel where my mat-menu-item is located. While I have successfully changed the color of my mat-menu-item, I am now faced with the challenge of changing th ...

Ways to determine the new position following a drop event using cdkDrag

Hey there, I'm looking to implement a drag and drop feature for some HTML elements but I'm struggling to capture the end position after dropping. After checking out the documentation, I see that with the use of the cdkDrag directive, there' ...

Incorporate the angular-cdk table functionality seamlessly with the sleek design of Bootstrap

Looking to enhance the design of my Angular application by incorporating Bootstrap table styling with the use of Angular CDK table (without Angular Material2). One challenge I've encountered is that while it's possible to add classes to the CDK ...

The Bootstraps CSS style doesn't seem to be working properly or being overridden within an Angular

I have seen similar questions asked before, but none of the solutions provided have fixed my issue. It appears that the styles are being applied initially but then overridden by something else, and I am unable to identify what is causing this. The only r ...

Utilizing Pipes within a Method in Angular 2 along with Dependency Injection triggers an "Insufficient Number of Arguments" error

I am searching for a solution to incorporate a custom pipe into my class. The custom pipe itself ( referenced from this source, many thanks ) involves injecting a dependency (the DomSanitizationService). import { Pipe, Inject, Injectable } from '@ang ...

Generating a hyperlink within an HTML file for other HTML files that reside in the same directory as the angular framework

Currently, I am developing a website for my final HTML project at university. This project uses Angular as the main framework. The task is to create 4 articles as separate HTML pages and have the article.component.html file contain links to these pages. I ...

Steps for setting up a voice and video chat system between VR devices and an Angular frontend

Hello, I am currently developing an application that links VR devices (Unity) with Angular frontends through the transmission of data to an Asp.net Core 3.1 API. The entire connection relies on SignalR Core to send JSON objects. However, a new requirement ...

What is the best way to create a routerlink that is both single-clickable and double-clickable within an

There have been numerous instances where a similar question has been raised, but I am unable to make this work. Despite looking at various answers such as this one on Stack Overflow that don't seem to work for most users. While this may not be specifi ...

Stay Alert: Angular Observable Continuously Monitored for Promise Updates

I am currently working on an angular application that has a connection with a printer. The printer possesses its own status service that is implemented as a promise, providing the printer's current status as a response. However, I am facing an issue w ...

Ways to leverage Composite API in place of Mixin or Extends functionality

Currently, I am exploring the use of Vue3 for my project. One issue I am facing is that I need to create multiple components (SFC) with similar properties. I want to define these common properties using the composite API across all components, like so: con ...

Encountered an issue with Angular 8 Firebase where an error was thrown at node_modules/firebase/index.d.ts(4369, 38): TS1005 error - expecting a sem

Having some trouble setting up Firebase on my Angular 8 application. I went through the following steps: 1. Deleted my node_modules 2. Ran npm install 3. Ran npm install --save firebase @angular/fire 4. Ran npm run build Encountering this error message: ...

Issue with calling Angular2 and jQuery autocomplete component methods from within a spec file

Utilizing the jQuery autocomplete method within an Angular2 component, I have created a service to fetch data from an API. Below is a snippet of myComponent.ts: export class myComponent { private myVar; private binding1; private binding2; constructor( @In ...

What is the best way to repurpose a variable in Angular's TypeScript?

I'm currently working on an application that utilizes the following technologies. In my Typescript file named "test.page.ts", there is a variable called "response: any" that I need to reuse in another Typescript file named "test2.page.html" by calling ...

Check out the computed typescript types

In my work with TypeScript types, I find myself frequently using Omit, Pick, and similar tools based on other types. While it generally gets the job done, I often struggle with readability when dealing with complex type manipulations. I am interested in f ...

Incorporate an Array of Objects into the UseState hook with an initial value

I have encountered an issue with the following error message: "Error: Objects are not valid as a React child (found: object with keys {fzfvhv76576, user }). If you meant to render a collection of children, use an array instead." I have been attem ...

Angular 2: Integrating a service into a class

I am working on an Angular class that represents a shape. My goal is to be able to create multiple instances of this class using a constructor. The constructor requires several arguments that define the properties of the shape. constructor(public center: ...