Multiple consecutive requests within another (Angular)

Currently, I am deepening my understanding of rxjs and struggling to find an efficient way to manage a sequence of requests. For instance, let's consider the UserService where one of its functions retrieves a user object based on the user id. After obtaining this object, I want to utilize its data to make multiple post requests in the following manner:

this.userService.getUser(5).subscribe(
  user => this.userService.sendEmail(this.makeEmailMessage(user.id, user.name, user.email)).pipe(
    delay(1000),
    tap(() => console.log('Sending an email'))
  ).subscribe(
    () => this.userService.sendSMS(this.makeSMSMessage(user.id, user.name, user.phone)).subscribe(
      () => console.log('Sending sms')
    )
  )
);

However, nesting subscribes like this can quickly lead to code that is difficult to follow and manage, especially when additional requests are involved.

A more structured approach would be to rewrite the above code as shown below:

this.userService.getUser(5).pipe(
  switchMap(
    user => {
      return forkJoin(
        this.userService.sendEmail(this.makeEmailMessage(user.id, user.name, user.email)).pipe(
          delay(1000),
          tap(() => console.log('Sending an email.'))
        ),
        this.userService.sendSMS(this.makeSMSMessage(user.id, user.name, user.phone)).pipe(
          tap(() => console.log('Sending an sms'))
        )
      );
    }
  )
).subscribe(
  res => console.log(res)
);

Using the forkJoin operator maintains the order of requests, even though the SMS request may finish first. If request order is important, then this code suffices, but what if preserving order is mandatory?

My main query pertains to the best practices for handling scenarios where request order matters versus situations where it doesn't. Additionally, in terms of error management, is it appropriate to handle errors using the catchError operator within the pipes (where tap was used)?

Answer №1

If maintaining the order of email and SMS is crucial, consider utilizing the switchMap operator to sequence HTTP requests.

   this.userService
      .getUser(5)
      // If an error occurs while retrieving user data, immediately handle it within the subscribe block to prevent sending any notifications
      .pipe(
        switchMap(user => {
          return this.userService
            .sendEmail(this.makeEmailMessage(user.id, user.name, user.email))
            .pipe(
              // catchError((err, obs) => of({})) can be used to still send SMS and Push notification even if sending email fails
              delay(1000),
              switchMap(() =>
                this.userService.sendSMS(
                  this.makeSMSMessage(user.id, user.name, user.phone)
                )
                // .pipe(catchError((error, obs) => of({})) can be implemented to send Push notification even if sending SMS fails
              ),
              switchMap(() =>
                // Push Notification should be sent last
                this.userService.sendPushNotification(
                  this.makePushNotificationMessage(
                    user.id,
                    user.name,
                    user.phone
                  )
                )
              )
            );
        })
      )
      .subscribe({
        next: next => {
          console.log(next);
        },
        error: error => {
          console.error(error);
        }
      });

It is advised to approach using the catchError() operator with caution as mishandling it may suppress valuable error messages and complicate debugging. Ensure that all errors are appropriately logged either in the console or a logging system when using catchError.

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

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

What causes the constant reappearance of props as a parameter in my Home function?

I'm currently in the process of developing an app, and I keep encountering an error related to "props" in my index.tsx file 'props' is declared but its value is never read.ts(6133) Parameter 'props' implicitly has an 'any&apos ...

Utilizing dynamic data within the ng-template

As a newcomer to Angular, I am facing a challenge in passing a string into a template. My goal is to create a reusable template or component that can display instructions on how to proceed with an action. Currently, I am achieving this by using the follow ...

From where does useTranslate fetch the translations?

I have started my journey to learn React with NextJS and recently purchased this amazing template. While exploring the src/pages/terms.tsx file, I came across some quite complex code. One thing that intrigued me was the question: What does the ? in conten ...

Configuring CORS settings in Angular-cli

Upon making a request to my backend url http://docker-users:5500/users?all=true, which returns a list of users, I encountered an issue with Angular-CLI's localhost url: http://localhost:4200. Even after configuring the proxy.config.json file and addin ...

Testing Angular components using mock HTML Document functionality is an important step in

Looking for help on testing a method in my component.ts. Here's the method: print(i) { (document.getElementById("iframe0) as any).contentWindow.print(); } I'm unsure how to mock an HTML document with an iframe in order to test this meth ...

Using a variable within an Angular 2 Component provider

Is it possible for a component to offer a service based on its inputs? In my situation, my component relies on a service provided by another firebase service, and I am looking to have the component create a new firebase service with a child element, allowi ...

Route user based on login status using router

I want to set up automatic routing to a login page for users who are not logged in. app.module.ts import { RouterModule, Routes } from '@angular/router'; import { AppComponent } from './app.component'; import { LoginComponent } from &ap ...

Checking for a property not present in the HTML when pushing a button in Angular 7 for form

How can I implement form validation based on a button push instead of input values? I have a nested form group with a button ("Confirm") and I need the parent form to be invalid until the button on the child is clicked. The standard validator seems to only ...

How to inject a regular class in Angular2 without the @Injectable decorator

angular: 2.0.0-beta.9 Can we inject a non-@Injectable class into a component? For instance, if this class originates from an external Third party library. ...

Encountering issues while trying to execute npm and node commands within Visual Studio Code

When I attempt to execute node commands in a command window, such as ng serve -o, everything works fine. However, when I try to do the same in VS Code, I encounter the following error message: ng : The term 'ng' is not recognized as the name of ...

Angular | Creating a template reference variable for a division element

Utilize the code provided to create 4 divs each sized at 200x200. In order to apply a specific class to a div when the mouse is hovering over it (without affecting the other three), you need to follow these steps: <style> div {height: 200px; wid ...

Accessing Webpack bundles using an "@" symbol for imports

I am currently working on bundling a Node Express server that was created using TypeScript and is being packaged with Webpack. Everything seems to be running smoothly when I compile/transpile the code into one JavaScript file called server.js. However, af ...

What is the best way to retrieve the file path of a imported Angular module?

I am trying to figure out how to retrieve the path of the BarComponent within the code snippet below. Specifically, I need to obtain '../bar/bar.component'. When dealing with a module loaded from a package such as Component module, I would like t ...

What could be the reason for mat-autocomplete not displaying the loading spinner?

Currently, I am integrating Angular Material into my Angular 11 project. One of the pages includes a mat-autocomplete component where I want to display a loading spinner while waiting for a request. Here is the code snippet I am using: component.ts this. ...

Retrieve all objects of the selected value using Angular autocomplete when an option is selected

I am currently working with an autocomplete component. I am passing an array of objects and would like to retrieve all item information (option) when an option is selected, not just the field value (option.name). <form class="example-form"> ...

Error encountered during TypeScript compilation: Module 'fs' not found

I encountered an issue: TSError: ⨯ Unable to compile TypeScript: server/src/test/test.ts(2,45): error TS2307: Cannot find module 'fs' Every time I execute this particular test import "mocha" import { writeFileSync, readFileSync } from &a ...

An error in npm occurred: "The name "@types/handlebars" is invalid."

While attempting to install typedoc using npm, I encountered the following error: npm ERR! Invalid name: "@types/handlebars" To resolve this issue, I proceeded to install @types/handlebars directly by running: npm install @types/handlebars However, I r ...

Incorporating Only XSD Files into an HTML Input Tag: A Simple Guide

Is there a way to restrict a file input element to only display XSD files? I attempted the following: <input type="file" accept="text/xsd" > Unfortunately, this method is not working as it still allows all file formats to be disp ...

Unable to loop through array generated by service - potential async complication?

In my service, I am populating an array of items by calling a function like this: items: IItem[] = []; ... LoadItems() { this.GetItems().subscribe((res) => { // console.log(res); if (res.status == 200 && res.body. ...