Combining observables using RxJS forkJoin over pipes

I am facing an issue where I need to make multiple server calls to save data, with each subsequent call requiring some data from the result of the previous call. I attempted to use forkJoin but encountered a problem with the sequence of events not making sense to me. It seems like the issue lies in the .pipe() call where I am attempting to modify the input data for the next call.

This situation raises two main questions:

  1. Why is the output not aligning with my expectations?
  2. Is there a way to resolve this using forkJoin? (I acknowledge that there are alternative approaches to solving this problem, but I am specifically interested in making forkJoin work in this scenario)

Below is a snippet of the code, or you can view it on StackBlitz.


  let data: { [key: string]: any } = {};

  forkJoin(
      this.saveFirst(data).pipe(
        tap(_ => console.log('saveFirst pipe after')),
        tap(result => data.id = result.id)
      ),
      this.saveSecond(data).pipe(
        tap(_ => console.log('saveSecond pipe after')),
        tap(result => data.name = result.name)
      ),
  ).subscribe(result => console.log('done: data - ', JSON.stringify(data)));

...

  private saveFirst(data: { [key: string]: any }): Observable<any> {
      console.log('saveFirst: start');
      console.log('saveFirst: data - ', JSON.stringify(data));

      return of({ id: 1 }).pipe(tap(_ => console.log('saveFirst: end')));
  }

  private saveSecond(data: { [key: string]: any }): Observable<any> {
      console.log('saveSecond: start');
      console.log('saveSecond: data - ', JSON.stringify(data));

      return of({ name: 'test' }).pipe(tap(_ => console.log('saveSecond: end')));;
  }

I was anticipating the following output:

saveFirst: start
saveFirst: data -  {}
saveFirst: end
saveFirst pipe after

saveSecond: start
saveSecond: data - {}
saveSecond: end
saveSecond pipe after

done: data -  {"id":1,"name":"test"}

However, the actual output I received was:

saveFirst: start
saveFirst: data -  {}
saveSecond: start
saveSecond: data -  {}

saveFirst: end
saveFirst pipe after
saveSecond: end
saveSecond pipe after

done: data -  {"id":1,"name":"test"}

Answer №1

To achieve the desired outcome, you should utilize either mergeMap or switchMap.

this.saveFirst(data).pipe(
          tap(_ => this.actions.push('saveFirst pipe after')),
          tap(result => data.id = result.id),
          switchMap((res)=>{
            return this.saveSecond(data).pipe(
          tap(_ => this.actions.push('saveSecond pipe after')),
          tap(result => data.name = result.name)
        );
          })).subscribe(result => this.actions.push('done: data - ' + JSON.stringify(data)));

The provided code snippet will deliver the expected result. When handling multiple requests and only focusing on the final output, consider using forkJoin.

Check out the modified version on Stackblitz.

Answer №3

Dealing with the same problem has been a real challenge for me, but I finally stumbled upon a solution that doesn't involve piping the observables. Instead, I handle all responses after subscribing to forkJoin.

This approach allows both tasks to run concurrently, rather than sequentially as in previous solutions. I transferred the handling from 'tap' to new methods, which are called once the forkJoin subscription is completed.

yourMethod()
{
    let data: { [key: string]: any } = {};

    forkJoin(
        this.saveFirst(data),
        this.saveSecond(data)
    ).subscribe(([res1, res2]) =>
    {
        this.handleSuccess_1(res1, data);
        this.handleSuccess_2(res2, data);
        console.log('done: data - ', JSON.stringify(data));
    });
}

handleSuccess_1(res, data)
{
    console.log('saveFirst pipe after');
    data.id = res.id;
}

handleSuccess_2(res, data)
{
    console.log('saveSecond pipe after');
    data.name = res.name;
}

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

Angular 6 component experiencing issues with animation functionality

I've implemented a Notification feature using a Notification component that displays notifications at the top of the screen. The goal is to make these notifications fade in and out smoothly. In my NotificationService, there's an array that holds ...

Interference of NestJS provider classes in separate event loops causing conflicts

I'm currently facing an issue where my shared library injectables are conflicting with each other. The bootstrap file initiates this file alongside a proxy server to start local microservices import { serviceA } from '@company/serviceA' imp ...

When trying to use preact as an alias for react, the error "Module not found: 'react/jsx-runtime'" is thrown

Avoid using the outdated guide I linked; follow the one provided in the answer instead I am trying to transition from react to preact by following their migration guide. I updated my webpack.config.js to include: alias: { "react": "pr ...

Utilizing next/image as a backgroundImage in a div container

Currently, I am working with nextjs and I am trying to set a background Image for a specific div using next/image. Most of the sources I found only explain how to implement full screen background images with next/image, not for a single div. I stumbled upo ...

Angular 4 get request using query strings

The initial code block is performing as anticipated fetchQuotes(): Observable<Quote[]> { return this.http.get(this.url) .map((res: Response) => res.json()) .catch((error: any) => Observable.throw(error.json().error || &apos ...

Instructions for displaying emotes and UTF-8 characters in a Flutter/Java app

In my efforts to display text in Flutter, I am facing the challenge of ensuring that the text is encoded in UTF-8 and can also show emojis. Unfortunately, I have not been successful in achieving both simultaneously. When attempting to decode an input with ...

What is the process for incorporating a new index signature into a class declaration from a file.d.ts in typescript?

I am facing an issue with a class in my project: // some npm module export class User { fname: string; lname: string; } Unfortunately, I cannot modify the declaration of this class from my project root since it is an npm module. I wish to add a new in ...

Angular 2 component hierarchy with parent and child components

Currently, I am in the process of learning typescript and angular2. My attempt to incorporate parent and child components into my fiddle has not been successful. Despite conducting some research, I have encountered an error that states: Uncaught ReferenceE ...

Why does the request body show as null even after passing body data in Prisma?

My application uses Prisma for database storage with MySQL. It is built using Next.js and TypeScript. I have set up the API and it is functioning properly. However, when I try to save data sent through the API, the `request.body` is null. Interestingly, wh ...

Modify the variable value only in React when the state undergoes a change

I'm facing a situation where I need to reset the stocksHelper class and instantiate it again whenever the component renders based on a change in the stocks' useState. This is essential because upon a change in stocks, a calculation needs to be pe ...

When the input type is set to 'number', FormGroup.valueChanges will only trigger on 'change' and 'blur' events

Issue: To replicate the problem, visit this Stackblitz link: https://stackblitz.com/edit/angular-feu1az The event handler is behaving unexpectedly when the value of the last field (of type number) is changed. Unlike Text 1 and Text 2 fields where the eve ...

Create Joi Schema based on TypeScript types/interfaces

Searching for a way to convert Typescript types or interfaces into joi schema objects led me to various solutions that did the opposite, such as generating Typescript types/interfaces from joi schemas. I came across options like ts-interface-builder and ts ...

Tips on automatically changing the background image every few seconds

As a newcomer to Angular and programming in general, I am facing an issue with changing the background image of my Page using the setInterval method. The intended behavior is for it to change every second, but for some reason, it changes much faster than t ...

What is the primary purpose of the index.d.ts file in Typescript?

There are some projects that include all types declarations within the index.d.ts file. This eliminates the need for programmers to explicitly import types from other files. import { TheType } from './somefile.ts' Is this the proper way to use ...

How can we ensure file uploads are validated when using class-validator?

Recently, I've been utilizing the wonderful class-validator package to validate data. One specific validation task I'm working on is validating a file upload, ensuring that the file is not empty and ideally confirming that it is an image file. H ...

Combining enum values to create a new data type

Exploring the implementation of type safety in a particular situation. Let’s consider the following: const enum Color { red = 'red', blue = 'blue', } const enum Shape { rectangle = 'rectangle', square = 'square ...

What is the best way to extract the value from a resolved Promise?

Currently, I am attempting to read a file uploaded by the user and convert it into a String using two functions. The first function is handleFileInput: handleFileInput(event){ setTimeOut(async()=>{ let abcd= await this.convertFileToString(this.fi ...

Retrieving the selected element from every division of a stack bar graph with multiple sections in Chart.js

I have created a stacked bar chart with three different sections or data sources. I am trying to figure out how to determine which section a user has clicked on. Here is the code from my .ts file: this.chart = new Chart("Chart1", { type: 'bar&ap ...

Issue: [HPM] Context not recognized. Please specify a valid context such as: "/api" or ["/api", "/ajax"]

Suddenly, I am encountering the following error in my Angular 7 project without making any changes. Strangely, it was working fine until yesterday. Error: [HPM] Invalid context. Expecting something like: "/api" or ["/api", "/ajax"] at Object.matchContext ...

Guide to utilizing Angular's translate i18n key as a dynamic parameter within HTML

Looking to pass an i18n translate service key as a parameter function on an HTML component. Attempted the following, but instead of getting the text, it's returning the key value. Created a variable assigned with the title in the component.ts file. ...