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

Is it possible to create a Facebook reveal tab using either Javascript or .NET?

As a developer who jumped into Facebook development just before the recent changes, I am feeling lost when it comes to building apps now. I see many questions similar to mine about creating fan-gating features using Javascript only. Is there an up-to-date ...

Retrieving information from an openDatabase using AngularJS

Encountering an issue while trying to retrieve data from openDatabase and display it in a listview. Following advice from a thread, I added $scope.$apply(); after $scope.items = $data;, but this resulted in an error: [$rootScope:inprog] $apply already in p ...

Angular's jQuery timepicker allows users to easily select a

Transitioning from jQuery to Angular, we previously utilized the for selecting times due to Firefox not supporting HTML5 input time. While searching for a similar timepicker plugin for Angular to maintain consistency with our past data and styles, I came ...

Encountered a problem while attempting to create a new project using angular/cli

I'm brand new to npm and Angular 2, and I'm attempting to set up a fresh Angular 2 project using angular/cli. My current setup includes: Node v8.9.3 npm v5.6.0 Windows 10 To start, I executed npm install -g @angular/cli successfully. Next, I n ...

Is there a way to prevent users from selecting certain days in ion-datetime?

After searching through the official documentation, I couldn't find a solution. I am in need of a function similar to the jQuery datepicker beforeshowday function. My goal is to disable all weekends (Saturday and Sunday) in upcoming dates so that user ...

How to eliminate undefined values from a dropdown selection in AngularJS

Blockquote When choosing a material from the column, the first option is showing as undefined. How can I remove undefined from the drop-down list? What changes need to be made in the HTML/JSON data for this to work properly? Blockquote var app = ang ...

What is the best location to initialize a fresh instance of the Firebase database?

Is the placement of const db = firebase.database() crucial in a cloud function script? For instance, in a file like index.ts where all my cloud functions are located, should I declare it at the top or within each individual function? const db = firebase. ...

ways to disrupt a timeout

I am attempting to incorporate a functionality similar to this example: https://www.w3schools.com/jsref/met_win_cleartimeout.asp On my personal website: If you enter certain characters in the input field, select one of them, and then pause for 5 seconds, ...

The continuous resizing of the window is triggering a loop in flexslider when the resize function is called

I am currently working on a website that utilizes the flexslider plugin. My goal is to detect when the browser window is resized, and then reinitialize the slider so that it can adjust its size and other parameters accordingly. Initially, I was able to a ...

How can I subtract a value from my array in Angular?

I've been troubleshooting this problem for a while now and I'm hoping that someone here can assist me with finding a solution. The issue at hand involves an array object containing various values such as id, title, amountCounter. Specifically, t ...

Is there a way to use ng-click to switch the ng-src of one image with that of another?

*I made updates to the plunkr and code to reflect my localhost version more accurately. It turned out that the AngularJS version was not the issue even after fixing the previous plunkr.* Let me start by saying that I am facing some challenges with Angular ...

When using Angular to send a request body containing IFormFile to an ASP.NET Core Web API, the properties are unexpectedly null

I am facing an issue with sending a request body as an object from Angular to ASP.NET Core Web API. All the properties are coming up as null except for ID. Here is the structure of my Web API: public class Book { public int BookID { get; set; } pu ...

Ways to customize PreBid.js ad server targeting bidder settings

In an effort to implement a unique bidder setting key name within my prebid solution, I have taken the necessary steps as outlined in the documentation by including all 6 required keys. Is it possible to change the key name 'hb_pb' to 'zm_hb ...

One should refrain from loading the API in Angular when there is no data present, by utilizing the global.getData method

Check out this code snippet: loadNextBatch() { console.log('scrolldown'); this.pageIndex = this.pageIndex + 1; this.global.getData(`/conditions/latest?start=${this.pageIndex}&length=${this.pageSize}`) .pipe(take(1)).subscr ...

Is there a way to utilize a Javascript function to incorporate commas into a div tag?

Being new to JavaScript, I have heard about jQuery but I don't know how to use it. Please bear with me as I am not well-versed in this area. :) I am currently importing data dynamically from an Excel database to a website. Everything is working fine ...

Using JavaScript drag and drop feature to remove the dragged element after a successful drop operation

Trying to figure out how to remove a dragged element from the DOM after a successful drop using the native JavaScript drag and drop API. After attempting to listen for the drop event, it seems that it only fires on the element being dropped onto and does ...

Send a JSON string directly to Google Cloud Storage without the need for a file upload

When I need to receive data in the form of a JSON string from an external source and then upload it directly to Google Cloud Storage without saving it as a local file first, is there a way to accomplish this task? Thank you. storage .bucket(bucketName) ...

Retrieve the JSON data from an AJAX request

I am a beginner in the world of AJAX and JavaScript. In one of my projects, I need to retrieve a JSON object in my JavaScript file. I have utilized spray-json library which displays the JSON object at this URL: http://localhost:8081/all-modules { "statu ...

Steps to load dynamic configuration in AngularFire2

I've been attempting to initialize angularfire2 with dynamic values, but I encounter errors when using aot. let _env = { apiKey: 'key...', authDomain: dynamicValueFromConfig, databaseURL: 'url...', ...

Change the behavior of JavaScript so that it executes when clicked, not when the

This script is currently set to run when the page loads: <script language="JavaScript"> j=parseInt(Math.random()*ranobjList.length); j=(isNaN(j))?0:j; document.write(unescape(ranobjList[j])); </script> Is there a way I can mak ...