Steer clear of chaining multiple subscriptions in RXJS to improve code

I have some code that I am trying to optimize:

someService.subscribeToChanges().subscribe(value => {
    const newValue = someArray.find(val => val.id === value.id)
    if (newValue) {
       if (value.status === 'someStatus') { 
          someArray.findIndex((a) => a.id === value.id)
          someArray.splice(index, 1);
       } else { 
          newValue.status = value.status;
       }
    } else { 
        if (value.someProp === 'abc') {
          someService.doSomeAsyncStuff().subscribe(data => {
            someService.handleData(data);
          });
        }
    }
 });

I attempted to refactor the code in order to avoid the second subscribe:

sub$ = someService.subscribeToChanges().pipe(switchMap(value => {
  const newValue = someArray.find(val => val.id === value.id)
  if (newValue) {  
       if (value.status === 'someStatus') { 
          someArray.findIndex((a) => a.id === value.id)
          someArray.splice(index, 1);
       } else { 
          newValue.status = value.status;
       }
  } else { 
    return iif(() => value.someProp === 'ABC', someService.doSomeAsyncStuff);
  }
}));

sub$.subscribe(data => someService.handleData(data))

This approach works when the iif condition is true. However, it halts the stream completely if false, contrary to what I desire which is to keep the stream flowing.

Answer β„–1

There are three different conditions in which you can divide them

sub$ = someService.subscribeToChanges().pipe(switchMap(value => {
 const newValue = someArray.find(val => val.id === value.id)
 if (newValue) {
   if (value.status === 'someStatus') { 
      someArray.findIndex((a) => a.id === value.id)
      someArray.splice(index, 1);
   } else { 
      newValue.status = value.status;
   }
 return of(value); 
 }

    if(value.someProp === 'ABC')
      return someService.doSomeStuff()

    return of(value)
  })
)

Answer β„–2

Nested subscriptions in rxjs are considered an anti-pattern, so it's best to avoid them. I suggest using two separate subscriptions instead of nesting them, especially if you have different side effects (such as someService.handleData and doSomeStuff).

To improve reusability, create a subscribeToChangesId with a comparator for the someArray id

const subscribeToChangesIdEqualsSomeArrayId$ = someService.subscribeToChanges().pipe(
  map(value => someArray.find(val => val.id === value.id))
);

Create an Observable for doSomeStuff

const doSomeStuff$ = subscribeToChangesIdEqualsSomeArrayId$.pipe(
  filter(newValue => newValue)
);

Create an Observable for handleData

const handleData$ = combineLatest([
  subscribeToChangesIdEqualsSomeArrayId$,
  someService.subscribeToChanges()
]).pipe(
  filter(([newValue, value]) => !newValue && value.someProp === 'abc'),
  switchMap(() => someService.doSomeStuff())
);

Usage Example

doSomeStuff$.subscribe(() => doSomeStuff());
handleData$.subscribe(data => someService.handleData(data));

Note: The variable names used here match those from your original question for clarity, but feel free to adjust them for better readability.

Answer β„–3

Keeping all logic in a single chain is always the best practice. You can achieve this by following a pattern like the one shown below. Since newValue is not required later in the chain, the process is quite straightforward:

someService.subscribeToChanges()
  .pipe(
    switchMap(value => {
      const newValue = someArray.find(val => val.id === value.id);
      if (!newValue && value.someProp === 'abc') {
        return someService.doSomeStuff();
      }
      return of(undefined);
    }),
  )
  .subscribe(data => {
    if (data === undefined) { // When data is set means we ran the second Observable
      doSomeStuff();
    } else {
      someService.handleData(data);
    }
  });

If undefined is a valid response for someService.doSomeStuff(), it’s advisable to wrap its response with an object structure such as { response: data } so you can easily trace its origin.

Avoid creating multiple subscriptions unless absolutely necessary, as it adds complexity and requires careful attention to details like subscription order and Observable sharing. Reading RxJS chains from top to bottom, similar to synchronous code, enhances readability.

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

Tips on how to dynamically load the content of a URL into a modal and update the browser address simultaneously

I am attempting to achieve a specific effect using JavaScript and jQuery Link to the content I want to load in a modal The goal is to load the content in a modal while keeping the main page in the background. Additionally, when a URL is followed, I want ...

"An easy way to dynamically update a select dropdown based on the data of the corresponding row in a table using jQuery

Having some trouble with dynamically populating form input fields in a table. The first row works fine, but when I change the select option in the second row, it affects the previous field instead of the current row: How can I ensure that changing the sel ...

React in 2022 has encountered an unexpected expression when it was expecting either an assignment or function call

I am having difficulty updating a chart with values received from an API. I am unsure about how to adjust the useEffect hook in my code. Any advice would be greatly appreciated. Here is a snippet of my code: import React, { useEffect, useState } from &quo ...

Hidden overflow and identification in URL causes content to be invisible and suddenly appear at the top of the page

I'm encountering a strange issue with containers that have overflow:hidden and when the page URL includes an id. The content gets pushed up and becomes invisible. This problem arises when I apply padding and negative margin at the bottom to create equ ...

Removing API request in React.js

My approach: deleteSample = () => { this.sampleService .deleteCall(this.props.id) .then((response) => { window.location.reload(false); }) .catch((error) => { console.log ...

Unable to activate the 'Click' function in Angular/Typescript after selecting element with 'document.querySelector'

In my Angular 8 Project, there is an element on a page with a unique string attached to the attribute 'data-convo-id'. This element has a click function that loads data based on the data attribute specified above. However, without direct access ...

How can I trigger a success event in JavaScript after ASP.NET form validation?

Is there a way for me to be notified when my ASP.NET form validation is successful so I can subscribe to that event? Here's the situation: When a user clicks a button on a payment form, I want to display a message saying "processing," but only if the ...

When the page hosted on Vercel is reloaded, `getStaticProps` does not function as expected

I'm currently working on a nextjs project and running into an issue where one of the pages returns a 404 error when it's reloaded. Within the page directory, I am using getStaticProps. pages - blogs.tsx - blog/[slug].tsx - index.tsx ...

Choose all from the list of categories

function CheckItems(chk) { if(document.myform.brandid.value!="Check all"){ for (i = 0; i < chk.length; i++) chk[i].checked = true ; document.myform.brandid.value="UnCheck all"; }else{ for ( ...

Does modifying data from an AngularJS service update the original data within the service?

Accidentally, I managed to create something that works but feels like it shouldn't! Essentially, I have a JSON data with a list of items that can be selected by the user, modified, and then saved. Here is a simplified version of the code: app.service ...

The rationale behind organizing analogous variables into groups

Currently, I am grappling with determining the optimal logic for grouping parameters within a specified tolerance level. Let me illustrate this with an example... Task1: parameter1=140 Task2: parameter1=137 Task3: parameter1=142 Task4: parameter1=139 Task ...

What is the best way to bring files into your project from a different directory?

Recently, I've been working on an app that consists of a backend repo and 2 frontend repos. Now, I'm facing the challenge of sharing code between these two frontend repos. app/ mySecondApp/ Despite my efforts, I'm unable to successfully imp ...

What is the most efficient way to transfer a value from the main application file to a router file using the express framework

Currently, I am developing an Express application with multiple routes. To ensure modularity, each route will have its own file stored in a dedicated routes folder. One challenge I encountered is sharing a common value across all routes. Specifically, I n ...

Ways to deselect a checkbox if the selected option is anything other than 'All'

I have a set of checkboxes generated from a database, but one checkbox labeled "All" serves as a way to select or deselect all other checkboxes with a single click. If all the options are checked and any individual option (other than 'All') i ...

Display the Angular Material sidebar within the parent component's closing tag

I am using an angular material sidenav, but every time I open it, the sidenav is always positioned at the left side of the window However, I want the sidenav to always open at the end of the component with a blue background similar to this screenshot: He ...

Tips for customizing the Electron title bar and enabling drag functionality

Currently, I am embarking on an electron project and my goal is to incorporate a unique custom frame at the top. Does anybody possess knowledge on how this can be achieved? To further clarify, here is a visual representation of what I envision for the cust ...

Troubleshooting Guide: Issues with Bootstrap 3 Modal Window Implementation

This question is so simple that it's embarrassing. I attempted to copy the code from http://getbootstrap.com/javascript/#modals directly into a basic page setup, but it's not functioning. It seems like I'm making a very silly mistake. Here i ...

Utilize an image in place of text (script type="text/javascript")

The vendor has provided me with some code: <a class="sh_lead_button" href="https://107617.17hats.com/p#/lcf/sfrnrskrvhcncwvnrtwwvhxvzkrvzhsd" onclick="shLeadFormPopup.openForm(event)">FREE Puppies</a> <script type="text/javascript" src="htt ...

Tips for retrieving arguments within a method called from a template in vue.js?

Here is an example where I am attempting to call a method from the template and pass in some arguments. How can I access those arguments from within the method? Snippet from script: methods: { showBinaries(job, id) { let test_url = process.en ...

Releasing Typescript 2.3 Modules on NPM for Integration with Angular 4

Although there are instructions available in Writing NPM modules in Typescript, they are outdated and there are numerous conflicting answers that may not be suitable for Angular. Additionally, Jason Aden has delivered an informative presentation on youtu ...