Which rxjs operator should be used when dealing with nested subscriptions in the presence of an if statement?

In my Angular/Typescript project, I am dealing with 2 subscriptions. Each subscription is subscribing to its own observable A and B, which are located outside the component in the service file.

Sometimes, when A changes, B may or may not change based on certain component actions, and vice versa. Typically, B changes after A changes in another component.

In addition, B must perform an action only after verifying the correct value of A. My initial approach involved nested subscriptions with an if statement as shown below:

ngOnInit(){
    this.A = this.serviceX.a.subscribe(
      data =>{ 
        this.temp = data;
        if(data=='correct'){
            this.B = this.serviceX.b.subscribe(
                beta => {
                    console.log('Sure'+this.temp);
                }
            )
        }
      }
    )
}

Although this approach works, I have come across posts suggesting it may not be the most efficient way. Thus, I experimented with combineLatest and forkJoin, but found that they only trigger if both observables change simultaneously. If one observable remains unchanged, these operators do not execute. Please correct me if I'm mistaken.

Currently, my solution involves separate subscriptions executed sequentially like so:

this.A = this.serviceX.a.subscribe(
  data =>{ 
    this.temp = data ;
  }
)
this.B = this.serviceX.b.subscribe(
  beta =>{ 
    if(this.temp=='correct'){
        console.log('Sure'+this.temp)
    }
  }
)

However, I am concerned about potential instability in this method. Which RxJS operator should I utilize to ensure that subscription B only occurs after A has completed changing (even though A may not change at times), thereby enhancing the overall robustness of the procedure?

Answer №1

It is advisable to avoid subscribing inside a subscribe-handler as it can become messy. The basic workflow, based on your code, follows these steps:

  1. Retrieve data from serviceX.a
  2. Store the data as a side effect
  3. If the data meets certain conditions, retrieve data from serviceX.b
  4. Actual subscription handler

You can find suitable operators for each step of the process. Here's how I would approach it:

this.serviceX.a.pipe(                 
  tap(data => this.temp = data),      
  filter(data => data == 'correct'),  
  concatMap(() => this.serviceX.b)    
).subscribe((beta) => 
  console.log('Sure' + this.temp)     
); 

Important Note: Only one subscription is required in this scenario.

Note 2: Depending on your requirements, you may consider replacing concatMap with switchMap, exhaustMap, or mergeMap.

Note 3: Be cautious when using temp as it can lead to race conditions depending on the *map-operator chosen. If the sole purpose of temp is to store the result from serviceX.a, consider an alternative solution outlined here: Is there an operator that lets me keep the initial value from a subscription after executing switchMap?

Answer №2

Perhaps you are searching for the usage of flatMap, where you can map multiple subscriptions, each flatMap should return its own Observable and only be subscribed to at the end.

this.serviceX.a.pipe(flatMap(data => {
  this.temp = data;
  if (data == 'correct') {
    return this.serviceX.b.pipe(
      flatMap(beta => {
        this.temp2 = beta;
        console.log('Sure' + this.temp);
        return this.serviceX.c.pipe(map(delta => {
          this.temp3 = delta;
          console.log('Sure' + this.temp2);
          return delta;
        }));
      }));
  }
})).subscribe(delta => {
  console.log('Complete all subscriptions');
});

Answer №3

Avoid nesting a subscribe within another subscribe as it can lead to issues such as breaking the stream and creating multiple subscribers. This can result in memory problems and inefficient code. Instead, opt for using operators like mergeMap or switchMap to handle your streams effectively with just one Subscribe at the end.

To filter your stream, utilize the filter operator for better stream management.

this.serviceX.a.pipe(                 
  filter((data: any) => data === 'correct'),  
  switchMap((value: any) => this.serviceX.b)     
)
.subscribe((value: any) => 
  console.log('Success' + this.temp);     
); 

By following this approach, you'll have only one subscriber that requires Unsubscribing, resulting in cleaner and more efficient code.

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

Error in Writing Functional Components with Typescript in React

I am struggling to create a versatile functional component and encountering issues like the one shown in this https://i.stack.imgur.com/WQkKg.png: Here is the code snippet: interface IAppTable<Type> { height: number; data: Type[]; tableLayout: ...

Guide to using Enums in *ngIf statements in Angular 8

I have defined an enum type in my TypeScript file, and I want to use it as a condition in my HTML code. However, when trying to access the "values" of the enum, they appear to be undefined even though I have declared them and inherited from the exported en ...

Variations in comparing tuple types in TypeScript

Exploring the TypeScript Challenge, there is a particular problem known as IsNever. The task at hand is to create a type called IsNever that takes input of type T. If the resolved type equates to never, the output should be true; otherwise, it should be fa ...

Utilizing localstorage data in angular 2: A comprehensive guide

Is there a way to utilize data stored in localstorage for another component? This is what the localstorage service looks like: localStorage.setItem('currentUser', JSON.stringify({ username: username, token: success, res: res.data })); I am inte ...

Encountering an issue with a custom hook causing an error stating "Attempting to access block-scoped variable 'X' before its declaration."

Currently, I am in the process of developing my initial custom hook. My confusion lies in the fact that an error is being displayed, even though the function is defined just a few lines above where it's invoked. Here is the relevant code snippet: f ...

Creating a layered image by drawing a shape over a photo in Ionic using canvas

While there are plenty of examples demonstrating how to draw on a canvas, my specific problem involves loading a photo into memory, adding a shape to exact coordinates over the photo, and then drawing/scaling the photo onto a canvas. I'm unsure of whe ...

Cross-origin resource sharing (CORS) implemented in an environment combining Electron,

I'm currently working on a desktop application using Electron with Angular for the front end and node.js (express server) for the backend API. The node.js express server is running at http://localhost:3000, while the Angular app wrapped inside electr ...

Tips for verifying the Reactive formControl/formArray when submitting

In my scenario, I am working with a FormGroup titled Parent, which includes a FormArray as a control. This FormArray consists of another FormGroup referred to as the Child. Main Goal The main objective here is to perform validation on all controls of a sp ...

Creating HTML content in TypeScript with NativeScript using document.write()

Essentially, I am looking to create a set number of labels at various row and column positions depending on the user's input. However, I have been unable to find any resources that explain how to write to the .component.html file from the .component.t ...

Angular: Card disabled post-click

I am displaying three cards <div *ngFor="let catalog of catalogs;let i=index" (click)="goToProducts(catalog)"> <div> <div class="name-position text {{catalog.classe}}" style="font-size: 21px;"> ...

Communicating between different components in Angular 11 using a service to share data

In my Angular 11 project, componentB has multiple methods that need to be called from componentA. Although I am aware that transferring these methods to a service would be the proper solution, it requires extensive effort which I want to avoid for now. In ...

Guide to customizing default selections in Plotly modeBar

I am currently developing an Angular 6 application that utilizes Plotly.js to create a scatter plot. One of the requirements I have is to set the default modeBar selection to "Show closest data on hover" (hoverClosestCartesian as mentioned in the document ...

The module '@angular/compiler-cli/ngcc' is missing and cannot be located while trying to run ng serve

Out of the blue, I started encountering this error. It seems to be related to a version issue with angular-cli, but I'm unable to pinpoint the exact problem. Any assistance would be greatly appreciated! npm i displays some warnings and one compiler e ...

Vue textarea not accepting null values

My current setup includes the following dependencies: - "vue": "3.2.26", - "vee-validate": "4.5.6", - "typescript": "4.5.4" While working on a textarea field in vue3, I encountered an issue Here's a snippet with vee-validate integration import { Fie ...

The specified type 'undefined' cannot be assigned to the type '"default" | "red" | "green" | "blue"

I am currently developing an app using React and TypeScript. Can someone help me troubleshoot and resolve the error message below? import { styled } from "linaria/react"; type Color = { color: "default" | "red" | "gree ...

The Angular project failed to run properly following the ng build command

Just started working with Angularjs 2 and encountered an issue after running ng build. The compiled files were placed in the dist folder, but when I checked the index.html file within that folder, all the scripts had missing references even though they w ...

Is there a workaround for the React useContext issue in Typescript aside from using <Partial>?

I am currently working on a React app that utilizes the useContext hook, but I am facing challenges with correctly typing my context. Here is the code snippet in question: import React, { useState, createContext } from 'react'; import endpoints f ...

What are the benefits of using default ES module properties for exporting/importing compared to named module properties?

Currently studying the Material UI documentation, I came across this statement: It is noted in the example above that we used: import RaisedButton from 'material-ui/RaisedButton'; instead of import {RaisedButton} from 'material-ui&apo ...

What steps can I take to prevent receiving the error message "Certain components in XXX are not associated with the entity" in Strapi?

User I am facing an issue with my application's endpoint for adding a like to a post. The endpoint is supposed to receive the user id who liked the post and insert it, along with the number of likes (not crucial at this moment), into a database. To ac ...

The module '@/assets/icons/pay/pay-success.png' cannot be located, along with its corresponding type declarations.ts

Recently, I encountered an issue while trying to import a png image in my Typescript code. Here is the snippet of code that caused the error: import paySuccessIcon from "@/assets/icons/pay/pay-success.png"; When I tried to import the image, Visual Studio ...