Joining subscriptions: How to synchronize completion of multiple subscriptions in a single operation

As a newcomer to RXJS, I am facing a particular challenge that I need help with.

I have two API calls, where the second call is dependent on the result of the first one. My issue lies in needing to handle both calls within a single subscription so that the finalize function triggers only after both subscriptions are completed. Currently, the finalize function triggers after the first observable finishes, not waiting for the second one to complete as well.

private getTemplate(){
this.loading = true;
this.service.getTemplate()
.pipe(
  finalize(() => this.loading = false)
)
.subscribe(
  (response) => {
    if (response) {
       this.createImage(response.link);
    }
  }
)

}

public createImage(link: string) {
this.service.createImage(link)
.subscribe(
  (response) => {
    this.image = response;
  }
)

Answer №1

To handle this situation, I recommend using a switchMap approach.

private manageData(){
  this.loading = true
  this.myService.getData().pipe(
    filter(response => !!response),
    switchMap(response => this.processData(response.link)),
    finalize(() => this.loading = false)
  ).subscribe()
}

public processData(link: string) {
  return this.service.processLink(link).pipe(
    tap(response => {
      this.data = response
    })
  )
)

The switchMap function will wait for the first observable to emit and then trigger the second one. By including the finalize() within the same pipe(), it will execute after the second observable is emitted.

It's crucial to ensure that the Observable is returned from processData so it can be utilized inside the pipe().

In the initial code snippet, the .subscribe() does not have any content. You can move the finalize logic here for the same behavior.

private manageData(){
  this.loading = true
  this.myService.getData().pipe(
    filter(response => !!response),
    switchMap(response => this.processData(response.link))
  ).subscribe(() => {
    this.loading = false
  })
}

If you need to pass data from getData to the subscribe() callback:

private manageData(){
  this.loading = true
  this.myService.getData().pipe(
    filter(response => !!response),
    switchMap(response => 
      combineLatest([of(response), this.processData(response.link)])
    )
  ).subscribe(([responseData, processedData]) => {
    this.loading = false
  })
}

Answer №2

If you want to subscribe to the second stream based on the result of the first, you can use switchMap. To ensure that you only proceed to the second stream if the first response is valid (if (response)), you can utilize filter.

An example implementation could look like this:

private fetchTemplate() {

  this.loading = true;
  this.networkService.getTemplate().pipe(

    filter(response => !!response),
    switchMap(resp => this.imageService.createImage(resp.link)),
    finalize(() => this.loading = false)

  ).subscribe(resp => this.imageData = resp);
  
}

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

Are additional Angular tags causing issues with the HTML layout in development mode?

When working with templates, I often find myself inserting two components inside the template of another one. Take for example this code snippet from my.component.html: // my.component.html <div class="container"> <div class="row& ...

Personalized Functions Using Material Design Expansion Panel Header Icon

Here's a unique material design accordion with an icon added to the panel header. I want to invoke my function 'myFun()' on click event, but currently, the expansion panel is toggling each time it's clicked (which is not the desired beh ...

How to efficiently manage multiple input fields with a single ref in React using TypeScript

I'm attempting to use the same reference for multiple input fields in my form. However, when I log it, the ref only points to the first input field. Is there a way I can share the same ref across different inputs? import React, {FC, useEffect, useRef, ...

Do attributes from our Angular app's <head> and <meta> tags get inherited by asynchronously pulled in HTML?

Within an Angular application, our index.html file contains a <head> tag where we define the charset: <head> <meta charset="utf-8"> </head> When a user navigates to a specific page, the component of that page makes an ...

What is the best way to display multiple items on a single page using the Ant Design (NG-Zorro) carousel component?

Hey there, I'm looking for a way to display multiple items per page using the ant design (NG-Zorro) carousel. I found some information on their website here: What I'm aiming for is to have something like this - Multiple Items If you have any i ...

What can be done to stop the default route from redirecting to "#/"?

Upon visiting http://localhost/, I have noticed that it automatically redirects me to http://localhost/#/. Is there a way to prevent this redirection and keep the URL at http://localhost/? Furthermore, is it possible to load a default component when the ...

Angular service fails to change the object's value

Currently, I am working on a service that is responsible for setting and getting the value of an object. The issue I am facing is that even though I am successfully sending the value to the setter function, when I try to retrieve it later, the values remai ...

Move up to the next level with Angular2 Bootstrap Pagination

I recently encountered an issue with the Angular2 bootstrap module. Interestingly, I was using the same module across different pages. However, when I decided to move the module to a higher level, the following error popped up: ERROR Error: Uncaught (in ...

Encountering a ReferenceError while attempting to implement logic on a newly created page

I've been experimenting with building a website using the Fresh framework. My goal was to add a simple drop-down feature for a button within a navigation bar, but I'm struggling to figure out where to place the necessary code. I attempted creatin ...

Utilize Angular Ag-grid's feature called "Columns Tool Panel" to trigger checkbox events

How can I capture the check/uncheck/change event of side panel columns? I have reviewed the documentation at https://www.ag-grid.com/angular-data-grid/tool-panel-columns/, but couldn't find any information on this. This relates to the Enterprise ver ...

What is the best way to extract accurate information from an observable?

I'm having trouble retrieving the correct data from an observable in my project. Here is the code for my API call: getLastxScans(amount: number): Observable<Scan[]> { return this.http.get<Scan[]>(`${this.ROOT_URL}/Scan/amount/${amo ...

Failed to divide the value into separate substring fields

I'm a beginner in working with typescript, and I'm currently attempting to extract data from fields in DynamoDB using typescript. My goal is to split a string field into two substrings and store them in separate variables. Here is an example of ...

Implementing dynamic data updates for the yAxis in a chart using Highcharts and Angular 2/4

I am currently working with a spline chart: this.chart = { chart: { type: 'spline', zoomType: 'x', animation: true, marginRight: 10, renderTo ...

Quietly renewing leads to OAuthErrorEvent with the type "silent_refresh_timeout" and no information on the reason or params

Upon calling the connect/authorize endpoint for silent renew, it triggers the silent_renew.html file. However, the log is showing an OAuthErrorEvent with type: "silent_refresh_timeout", reason: null, params: null. I am utilizing an angular client with the ...

Attempting to establish a connection with MongoDB through Realm

Exploring Realm and MongoDB for the first time has been an interesting journey for me. I began by following a helpful tutorial as a starting point for my project. Here is the link to my project structure on CodeSandbox The folder structure includes: src ...

Why does TypeScript struggle to accurately deduce the return type when provided with certain parameter values?

I have a function that uses a switch case to return different results depending on the input. The function, called "getTimeAgo," takes in two parameters: "date" (which can be either a Date object or a string) and "mode" (which can only be either "days" or ...

The Redux state fails to update on the initial attempt

I have encountered an issue with my state setup and reducer logic. Here is how my state is initialized: const initialState: PhotoState = { photos: [], }; The reducer function is defined as follows: const initialState: PhotoState = { photos: [], }; ex ...

Guide to creating an Express + TypeScript API using an OpenAPI 3.0 specification

After creating specifications for my REST API server using OpenAPI 3.0, I found myself wanting to generate an expressjs app quickly instead of manually writing repetitive code. However, the generated code from editor.swagger.io is in javascript, which does ...

I am having trouble with CSS, JS, and image files not loading when using Angular 9 loadChildren

I am facing an issue with loading CSS, JS, and images in my Angular 9 project. I have separate 'admin' and 'catalog' folders where I want to load different components. However, the assets are not loading properly in the 'catalog&ap ...

What is the best way to define a function that accepts an object with a specific key, while also allowing for any additional keys to be passed

Typescript allows us to define an interface for an object that must have a key and can also allow additional keys: interface ObjectWithTrace { trace: string; [index: string]: any } const traced: ObjectWithTrace = { trace: 'x', foo: 'bar ...