Which is the optimal choice: subscribing from within a subscription or incorporating rxjs concat with tap?

After storing data in the backend, I proceed to retrieve all reserved data for that specific item.
It is crucial that the data retrieval happens only after the reservation process to ensure its inclusion.

Presented with two possible solutions, I am contemplating which one is superior and whether utilizing concat with tap provides any advantages in this scenario:

Using subscription inside another subscription:

...
this.reserveDataItem();
...

reserveDataItem(){
   this.myService.reserveData(this.selectedData).subscribe(res => {
     if (res.status == 'SUCCESS'){
       ...logSuccessMessagesAndOtherStuff
     }
     this.getReservedDataItemsId();
   });
 }


getReservedDataItemsId(){
   this.myService.getReservedDataItemsForRequestId(this.requestId).subscribe(res => {
     if (res.status == 'SUCCESS'){
       ..doStuffWithDataItems
     }
   });
 }

Implementing concat with tap:
I opted for tap due to difficulties in handling multiple return types within a single subscription
and thus, I am curious about any potential benefits of using this method..

...
concat(this.reserveDataItem(), this.getReservedDataItemsId())
   .subscribe();
...

reserveDataItem(): Observable<ApiResponseDto<any>>{
  return this.myService.reserveData(this.selectedData).pipe(
    tap(res => {
      if (res.status == 'SUCCESS'){
        ...logSuccessMessagesAndOtherStuff
      }
    })
  )
 }


getReservedDataItemsId():Observable<ApiResponseDto<DataItemDto[]>>{
   return this.myService.getReservedDataItemsForRequestId(this.requestId).pipe(
    tap(res => {
       if (res.status == 'SUCCESS'){
         ..doStuffWithDataItems
       }
     })
  )
 }

Answer №1

From what I gather from your inquiry:

After saving data on the backend (first Observable), you receive a response once the save operation is completed. Regardless of the response status, you intend to make another request following the completion of the first Observable. Only after the initial task is finished do you wish to retrieve this data.

If your reserveData method involves making a single HTTP request, my suggestion would be to employ switchMap:

"When utilizing switchMap, each inner subscription concludes when the source emits, allowing only one active inner subscription."
(from https://www.learnrxjs.io/)

Below is a simplified version of how I would address the problem:

myObs = new Observable((observer) => {
    observer.next({status: 'SUCCESS'});
    observer.complete();
});

myObs2 = new Observable((observer) => {
    observer.next({status: 'SUCCESS'});
    observer.complete();
});

this.myObs.pipe(
  switchMap((resp) => {
    if (resp.status === 'SUCCESS') ...logSuccessMessagesAndOtherStuff;
    return this.myObs2;
  })
).subscribe(resp => {
  if (resp.status === 'SUCCESS') ...doStuffWithDataItems;
})

I also have a related question that might offer some insight into your situation. Considering that an Http request/response yields 'only the last notification (the result from the HTTP request),' it seems unnecessary to utilize mergeMap or concatMap (assuming your reserveData method primarily makes one HTTP request and returns a response).

Answer №2

Switch to using forkJoin instead :

    const response1 = this.myService.reserveData(this.selectedData);
    const response2 = this.myService.getReservedDataItemsForRequestId(this.requestId);
    
    fetchData(): Observable<any> {
      return forkJoin([response1, response2]);
    }
    
    this.fetchData().subscribe(result => {
       //...handleSuccessMessagesAndMore result[0]
       //..processDataItems result[1]
    }, error => {
       console.log(error);
    });

Answer №3

Here is a way to structure your code without nesting subscriptions:

this.myService.reserveData(this.selectedData).pipe(
  mergeMap(res => {
    if (res.status == 'SUCCESS'){
      ...logSuccessMessagesAndOtherStuff
    }
    return this.myService.getReservedDataItemsForRequestId(this.requestId)
  })
).subscribe(res => {
  if (res.status == 'SUCCESS'){
    ..doStuffWithDataItems
  }
});

It's beneficial to avoid nesting subscriptions to prevent complex structures in the code. By using operators like map and filter, you can easily enhance your data pipeline with additional transformations or filters.

For instance, you can filter out unsuccessful results before processing them further as shown in the above code snippet.

this.myService.reserveData(this.selectedData).pipe(
  mergeMap(res => {
    if (res.status == 'SUCCESS'){
      ...logSuccessMessagesAndOtherStuff
    }
    return this.myService.getReservedDataItemsForRequestId(this.requestId)
  }),
  filter(res => res.status == 'SUCCESS')
).subscribe(res => {
  // No need to check for status here 
  ..doStuffWithDataItems
});

If you require specific formatting of data from the backend, you can achieve that by applying map operator to transform the data accordingly before using it.

this.myService.reserveData(this.selectedData).pipe(
  mergeMap(res => {
    if (res.status == 'SUCCESS'){
      ...logSuccessMessagesAndOtherStuff
    }
    return this.myService.getReservedDataItemsForRequestId(this.requestId)
  }),
  filter(res => res.status == 'SUCCESS'),
  map(res => ({
    firstName: res.userdata.fname[0].toUpperCase() + res.userdata.fname.toLowerCase().slice(1),
    lastName: res.userdata.lname.toUpperCase(),
    age: (2020 - res.userdata.birthyear)
  }))
).subscribe(user => {
  ..doStuffWithUser
});

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

The enigmatic occurrence of TypeScript decorators: when a decorator parameter mysteriously transforms into undefined in a particular scenario

I have been working on developing my own Object-Relational Mapping (ORM) system and I encountered an interesting issue in TypeScript that I am trying to understand. If anyone can provide some insight or clarification on this matter, it would be greatly app ...

Struggling to transfer information from JavaScript to Python Flask?

I am currently working on a basic configuration where I need to send a string to my Flask application through Ajax, but unfortunately, I am encountering Error code 400: Bad request. Here is the JavaScript code snippet: headers = { 'Content-type&a ...

How can I establish default values for 2 to 3 options in a Dropdownlist?

Is there a way to set two values as default in a dropdown list, and when the page is refreshed, the last two selected values are retained as defaults? Thanks in advance! Visit this link for more information ...

Vue.js: Issue with applying class binding while iterating over an object

I've been working with an object data that looks like this: object = { "2020092020-08-01":{ "value":"123", "id_number":"202009" }, "2020092020-09-01":{ "value& ...

Utilizing Angular 2's offline capabilities for loading locally stored JSON files in a project folder

I've been attempting to load a local JSON file from my Angular 2 project folder using the HTTP GET method. Here is an example of the code snippet: private _productURL = 'api/products/products.json'; getProducts(): Observable<any> ...

Is there a way to trigger a request to the backend when the user closes or refreshes the browser?

My Objective: I am working on a lobby feature that updates automatically when a player leaves. Using backend requests and sockets, the lobby is updated to display the current list of players after someone exits. The Challenge: I am faced with the issue ...

Generating HTML widgets dynamically with jQuery: A step-by-step guide

Looking for a way to display an interactive widget on your page while allowing users to generate multiple instances and interact with them simultaneously? Imagine having a widget like this: <div id="my_widget"> <script type="text/javascript" ...

Failed deployment of a Node.js and Express app with TypeScript on Vercel due to errors

I'm having trouble deploying a Nodejs, Express.js with Typescript app on Vercel. Every time I try, I get an error message saying "404: NOT_FOUND". My index.ts file is located inside my src folder. Can anyone guide me on the correct way to deploy this? ...

Updating Select Options with Multiple Values using Javascript

My goal is to update the selected value of multiple select elements simultaneously using JavaScript. However, I am facing an issue where my script only updates one select element instead of all of them on the page. Unfortunately, I cannot modify the id att ...

Angular 7's Cross-Origin Resource Sharing (CORS) Configuration

Hey there! I've been struggling with getting the CORS to work. I stumbled upon this helpful post that pointed me in the right direction. Following the link provided in that post to angular.io, I implemented the solution suggested. Let me describe my ...

Customize your Bootstrap navbar dropdown to showcase menu items in a horizontal layout

I have been developing a new project that requires a navbar to be positioned at the center of the page, at the top. The current HTML code I am using is as follows... <div class="navbar navbar-inverse navbar-fixed-top center"> <div class="con ...

Transitioning from an npm package to an npm symbol link in Angular libraries: A step-by-step guide

In my Angular setup, I have a single Application and one Library. The library is connected to the application through a symbolic link (npm link my-lib). I updated my tsconfig.json file to recognize the path of my library: "paths": { "my-l ...

What is the best way to prompt Leaflet to refresh the map display?

I'm facing a challenge while integrating Leaflet with React, where Leaflet seems to want control over the DOM rendering as well based on my research. Currently, I have countries being properly colored according to specific color codes derived from ba ...

Dynamic Material UI Timeline

I am facing a challenge with making the Timeline in Material UI responsive for the first time. Currently, I have it set to align 'alternate', but I want it to switch to align 'left' when viewed on mobile or certain screen widths. I have ...

The automation script for Playwright/Puppeteer is having trouble properly handling `async...await` in a `for..loop` on the `/signup` page

Currently, I am faced with the challenge of automating rate-limit requests using a playwright automation script. The issue arises when the script keeps attempting to sign up with the email <a href="/cdn-cgi/l/email-protection" class="__cf_email__" data ...

Clicking the submit button in JavaScript will trigger a request to the Spring MVC backend,

When I use the following code, it gives me an object response: @RequestMapping(value = "/NewLogin",method = RequestMethod.POST) public @ResponseBody Token getAllBooks( Token token = new Token(); token.setValue(encryptedMessage); return toke ...

What methods can be used to protect an application with spring boot and angular 8 without requiring users to log in?

I am looking to enhance security for an application that leverages angular 8 and spring boot 5. Currently, the application is free form and does not require login to access the UI. Although I have implemented CSRF protection, it is still possible for som ...

Using Filters in VueJs

I am currently working on creating a small Vue.js 2.0 application where I am utilizing the v-select component from here. The data format that I am dealing with looks something like this: { "model": [ { "id":1, ...

Permitted the usage of a global variable of any type as the return value of a function that does not explicitly define its

Here's a snippet of TypeScript code that compiles successfully: let testVar: any; const testFunc: () => number = () => { return testVar; }; Why does this code compile without errors? What is the reasoning behind it? ...

Prevent unauthorized entry to css and javascript files

Is there a way to prevent direct access to a file? I want the file to be used on my website, but I want to block it from being accessed directly. For example, if you try to open this link: https://example.com/style.css, you will see an error message. Howev ...